home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 2000 April: Mac OS SDK / Dev.CD Apr 00 SDK1.toast / Development Kits / Mac OS / Navigation Services SDK / Examples / SimpleText / SimpleText ƒ / TextFile.c < prev    next >
Encoding:
C/C++ Source or Header  |  1999-06-16  |  87.8 KB  |  3,184 lines  |  [TEXT/MPS ]

  1. /*
  2.     File:        TextFile.c
  3.  
  4.     Copyright:    © 1997-1998 by Apple Computer, Inc., all rights reserved.
  5.  
  6. */
  7.  
  8. /*
  9. **    You may incorporate this sample code into your applications without
  10. **    restriction, though the sample code has been provided "AS IS" and the
  11. **    responsibility for its operation is 100% yours.  However, what you are
  12. **    not permitted to do is to redistribute the source as "DSC Sample Code"
  13. **    after having made changes. If you're going to re-distribute the source,
  14. **    we require that you make it clear in the source that the code was
  15. **    descended from Apple Sample Code, but that you've made changes.
  16. */
  17.  
  18. #include "MacIncludes.h"
  19.  
  20. #include "TextFile.h"
  21.  
  22.  
  23. #include "NavigationServicesSupport.h"
  24.  
  25. #pragma segment Text
  26.  
  27.  
  28.  
  29. // --------------------------------------------------------------------------------------------------------------
  30. // INTERNAL DEFINES
  31. // --------------------------------------------------------------------------------------------------------------
  32. #define kOnePageWidth         600            // preferred width of a window
  33. #define kMargins            4            // margins in window
  34. #define kPrintMargins        8            // margins in printing window
  35. #define kGXPrintMargins        10            // margins in printing a GX window
  36.  
  37. #define kPictureBase        1000        // resource base ID for PICTs in documents
  38. #define kSoundBase            10000        // resource base ID for 'snd 's in documents
  39.  
  40. // resources for controling printing
  41. #define kFormResource        'form'
  42.     #define kFormFeed            0x00000001    // form feed after this line
  43.  
  44. #define    kContentsListID        10000        // resource ID for STR# contents menu         
  45.  
  46. // some memory requirements
  47. #define kPrefBufferSize        90*1024
  48. #define kMinBufferSize        5*1024
  49.  
  50. #define kDeleteKey            8
  51. #define kForwardDeleteKey    0x75
  52.  
  53.  
  54. extern pascal void AsmClikLoop();
  55.  
  56. #define ABS(n)                (((n) < 0) ? -(n) : (n))
  57.  
  58. // --------------------------------------------------------------------------------------------------------------
  59. // GLOBALS USED ONLY BY THESE ROUTINES
  60. // --------------------------------------------------------------------------------------------------------------
  61.  
  62. // These variables are used for speech, notice that on purpose we only
  63. // support a single channel at a time.
  64. static SpeechChannel    gSpeechChannel = nil;        
  65. static VoiceSpec        gCurrentVoice;
  66. static Ptr                gSpeakPtr = nil;
  67. static Boolean            gAddedVoices = false;
  68. static Str31            gPictMarker1, gPictMarker2;
  69.  
  70. // --------------------------------------------------------------------------------------------------------------
  71. // EXTERNAL FUNCTIONS
  72. // --------------------------------------------------------------------------------------------------------------
  73.  
  74. extern OSErr    TextDragTracking(WindowRef pWindow, void *pData, DragReference theDragRef, short message);
  75. extern OSErr    TextDragReceive(WindowRef pWindow, void *refCon, DragReference theDragRef);
  76. extern Boolean    DragText(WindowRef pWindow, void *pData, EventRecord *pEvent, RgnHandle hilightRgn);
  77.  
  78. // --------------------------------------------------------------------------------------------------------------
  79. // INTERNAL ROUTINES
  80. // --------------------------------------------------------------------------------------------------------------
  81.  
  82. static void TextAddContentsMenu(WindowDataPtr pData);
  83. static void TextRemoveContentsMenu(WindowDataPtr pData);
  84. static OSErr TextGetContentsListItem(WindowDataPtr pData, short itemNum, StringPtr menuStr, StringPtr searchStr, short *totalItems);
  85. static OSErr TextAdjustContentsMenu(WindowDataPtr pData);
  86.  
  87. // --------------------------------------------------------------------------------------------------------------
  88. // UNDO UTILITY FUNCTIONS
  89. // --------------------------------------------------------------------------------------------------------------
  90. OSErr SaveCurrentUndoState(WindowDataPtr pData, short newCommandID)
  91. {
  92.     OSErr        anErr = noErr;
  93.     TEHandle    hTE = ((TextDataPtr) pData)->hTE;
  94.     
  95.     // this is only a new command if:
  96.     //    the command ID is different
  97.     // OR
  98.     //    the selection is a range, and not equal to the old one
  99.     if     (
  100.             ( ((TextDataPtr) pData)->prevCommandID != newCommandID )
  101.         ||
  102.             (
  103.                 ( (**hTE).selStart != (**hTE).selEnd )
  104.             ||
  105.                 ( ABS((**hTE).selStart - ((TextDataPtr) pData)->prevSelStart) > 1 )
  106.             )
  107.         )
  108.         {
  109.         Handle        tempHandle;
  110.         Size        tempSize;
  111.         
  112.         // if we don't have save handles, make em!
  113.         if (!((TextDataPtr) pData)->prevText)
  114.             {
  115.             ((TextDataPtr) pData)->prevText = NewHandle(0);
  116.             anErr = MemError();
  117.             nrequire(anErr, MakeTextSave);
  118.             }
  119.         if  (!((TextDataPtr) pData)->prevStyle) 
  120.             {
  121.             ((TextDataPtr) pData)->prevStyle = NewHandle(0);
  122.             anErr = MemError();
  123.             nrequire(anErr, MakeStyleSave);
  124.             }
  125.             
  126.         // grow the save handles and block move into them
  127.         tempHandle = (**hTE).hText;
  128.         tempSize = GetHandleSize(tempHandle);
  129.         SetHandleSize( ((TextDataPtr) pData)->prevText, tempSize);
  130.         anErr = MemError();
  131.         nrequire(anErr, GrowTextSave);
  132.         BlockMoveData(*tempHandle, * ((TextDataPtr) pData)->prevText, tempSize );
  133.  
  134.         tempHandle = (Handle) TEGetStyleHandle(hTE);
  135.         tempSize = GetHandleSize(tempHandle);
  136.         SetHandleSize( ((TextDataPtr) pData)->prevStyle, tempSize);
  137.         anErr = MemError();
  138.         nrequire(anErr, GrowTextSave);
  139.         BlockMoveData(*tempHandle, * ((TextDataPtr) pData)->prevStyle, tempSize );
  140.  
  141.         // save length
  142.         ((TextDataPtr) pData)->prevLength = (**hTE).teLength;
  143.         ((TextDataPtr) pData)->beforeSelStart = (**hTE).selStart;
  144.         ((TextDataPtr) pData)->beforeSelEnd = (**hTE).selEnd;
  145.         }
  146.     
  147.     // save start and end of the selection
  148.     ((TextDataPtr) pData)->prevSelStart = (**hTE).selStart;
  149.  
  150.     // if we didn't have any problems, then we can undo this operation
  151.     ((TextDataPtr) pData)->prevCommandID = newCommandID;
  152.     return(noErr);
  153.     
  154. // EXCEPTION HANDLING
  155. GrowStyleSave:
  156. GrowTextSave:
  157. MakeStyleSave:
  158. MakeTextSave:
  159.     // can't undo because of an error 
  160.     // (at some point might warn the user, but that's probably more annoying)
  161.     ((TextDataPtr) pData)->prevCommandID = cNull;
  162.     
  163.     return(anErr);
  164.     
  165. } // SaveCurrentUndoState
  166.  
  167. // --------------------------------------------------------------------------------------------------------------
  168. static void PerformUndo(WindowDataPtr pData)
  169. {
  170.     if (((TextDataPtr) pData)->prevCommandID != cNull)
  171.         {
  172.         TEHandle    hTE = ((TextDataPtr) pData)->hTE;
  173.         long        tempLong;
  174.         
  175.         // undo text
  176.         tempLong = (long) (**hTE).hText;
  177.         (**hTE).hText = ((TextDataPtr) pData)->prevText;
  178.         ((TextDataPtr) pData)->prevText = (Handle)tempLong;
  179.  
  180.         // undo length
  181.         tempLong = (long) (**hTE).teLength;
  182.         (**hTE).teLength = ((TextDataPtr) pData)->prevLength;
  183.         ((TextDataPtr) pData)->prevLength = tempLong;
  184.  
  185.         // undo style
  186.         tempLong = (long) TEGetStyleHandle(hTE);
  187.         if (HandToHand((Handle*) &tempLong) == noErr)
  188.             {
  189.             TESetStyleHandle( (TEStyleHandle) ((TextDataPtr) pData)->prevStyle, hTE );
  190.             ((TextDataPtr) pData)->prevStyle = (Handle)tempLong;
  191.             }
  192.             
  193.         // undo selection
  194.             {
  195.             short    start, end;
  196.             
  197.             start = ((TextDataPtr) pData)->beforeSelStart;
  198.             end = ((TextDataPtr) pData)->beforeSelEnd;
  199.             ((TextDataPtr) pData)->prevSelStart = (**hTE).selStart;
  200.             TESetSelect(start, end, hTE);
  201.             }
  202.         
  203.         }
  204.         
  205. } // PerformUndo
  206.  
  207.  
  208. // --------------------------------------------------------------------------------------------------------------
  209. // TEXT EDIT UTILITY FUNCTIONS
  210. // --------------------------------------------------------------------------------------------------------------
  211. static long CalculateTextEditHeight(TEHandle hTE)
  212. {
  213.     long    result;
  214.     short    length;
  215.     
  216.     result = TEGetHeight(32767, 0, hTE);
  217.     length = (**hTE).teLength;
  218.     
  219.     // Text Edit doesn't return the height of the last character, if that
  220.     // character is a <cr>.  So if we see that, we go grab the height of
  221.     // that last character and add it into the total height.
  222.     if ( (length) && ( (*(**hTE).hText)[length-1] == '\n') )
  223.         {
  224.         TextStyle    theStyle;
  225.         short        theHeight;
  226.         short        theAscent;
  227.  
  228.         TEGetStyle(length, &theStyle, &theHeight, &theAscent, hTE);
  229.         result += theHeight;
  230.         }
  231.  
  232.     return result;
  233.  
  234. } // CalculateTextEditHeight
  235.  
  236. // --------------------------------------------------------------------------------------------------------------
  237. void AdjustTE(WindowDataPtr pData, Boolean doScroll)
  238. {
  239.     TEPtr        te;
  240.     short        value;
  241.     short        prevValue;
  242.     
  243.     te = *((TextDataPtr) pData)->hTE;
  244.     prevValue = GetControlValue(pData->vScroll);
  245.     value = te->viewRect.top - te->destRect.top;
  246.     SetControlValue(pData->vScroll, value);
  247.  
  248.     te = *((TextDataPtr) pData)->hTE;
  249.     if (doScroll)
  250.         {
  251.         short    hScroll = te->viewRect.left - te->destRect.left;
  252.         short    vScroll = value - prevValue;
  253.         if ((hScroll != 0) || (vScroll != 0))
  254.             TEScroll(hScroll, vScroll, ((TextDataPtr) pData)->hTE);
  255.         }
  256.     
  257.             
  258. } // AdjustTE
  259.  
  260. // --------------------------------------------------------------------------------------------------------------
  261.  
  262. static void RecalcTE(WindowDataPtr pData, Boolean doInval)
  263. {
  264.     Rect    viewRect, destRect;
  265.     short    oldOffset;
  266.     
  267.     destRect = (**((TextDataPtr) pData)->hTE).destRect;
  268.     viewRect = (**((TextDataPtr) pData)->hTE).viewRect;
  269.     oldOffset = destRect.top - viewRect.top;
  270.     
  271.     viewRect = pData->contentRect;
  272.             
  273.     InsetRect(&viewRect, kMargins, kMargins);
  274.     destRect = viewRect;
  275.     
  276.     OffsetRect(&destRect, 0, oldOffset);
  277.     
  278.     (**((TextDataPtr) pData)->hTE).viewRect = viewRect;
  279.     (**((TextDataPtr) pData)->hTE).destRect = destRect;
  280.     
  281.     TECalText(((TextDataPtr) pData)->hTE);
  282.  
  283.     if (doInval)
  284.         InvalRect(&qd.thePort->portRect);
  285.     
  286. } // RecalcTE
  287.  
  288. // --------------------------------------------------------------------------------------------------------------
  289.  
  290. pascal TEClickLoopUPP GetOldClickLoop(void)
  291. {
  292.     return ((TextDataPtr) FrontWindow())->docClick;
  293.     
  294. } // GetOldClikLoop
  295.  
  296. pascal void TextClickLoop(void)
  297. {    
  298.     WindowRef    window;
  299.     RgnHandle    region;
  300.     
  301.     window = FrontWindow();
  302.     region = NewRgn();
  303.     GetClip(region);                    /* save clip */
  304.     ClipRect(&GetWindowPort(window)->portRect);
  305.     ((TextDataPtr) window)->insideClickLoop = true;
  306.         AdjustScrollBars(window, false, false, nil);
  307.     ((TextDataPtr) window)->insideClickLoop = false;
  308.     SetClip(region);                    /* restore clip */
  309.     DisposeRgn(region);
  310.  
  311. } // TextClickLoop
  312.  
  313. #if GENERATINGPOWERPC
  314. static pascal Boolean CClikLoop(TEPtr pTE)
  315. {
  316.     CallTEClickLoopProc(GetOldClickLoop(), pTE);
  317.     
  318.     TextClickLoop();
  319.     
  320.     return(true);
  321.     
  322. } // CClikLoop
  323.  
  324.     static RoutineDescriptor gMyClickLoopRD = BUILD_ROUTINE_DESCRIPTOR(uppTEClickLoopProcInfo, CClikLoop);
  325.     static TEClickLoopUPP gMyClickLoop = &gMyClickLoopRD;
  326. #else
  327.     static TEClickLoopUPP gMyClickLoop = NewDrawHookProc(AsmClikLoop);
  328. #endif
  329.  
  330. // --------------------------------------------------------------------------------------------------------------
  331. #if GENERATINGPOWERPC
  332. pascal void MyDrawHook ( unsigned short offset, unsigned short textLen,
  333.      Ptr textPtr, TEPtr tePtr, TEHandle teHdl )
  334. #else
  335. pascal void MyDrawGlue();
  336.  
  337. pascal void MyDrawHook ( TEHandle teHdl, TEPtr tePtr, Ptr textPtr, short textLen, short offset )
  338. #endif
  339. {
  340. #pragma unused    ( teHdl, tePtr)
  341.  
  342.     register short            drawLen = 0;
  343.     register Ptr            drawPtr;
  344.     
  345.     drawPtr = textPtr + (long)offset;
  346.     
  347.     while ( textLen--)
  348.         {
  349.         if ( *drawPtr == 0xCA &&
  350.              ( *(drawPtr - 1) == 0x0D || *tePtr->hText == textPtr) )
  351.             {
  352.             DrawText( textPtr, offset, drawLen);
  353.             DrawChar( '\040');
  354.             offset += drawLen + 1;
  355.             drawLen = 0;
  356.             }
  357.         else
  358.             {
  359.             ++drawLen;
  360.             }
  361.         ++drawPtr;
  362.         }
  363.     
  364.     DrawText( textPtr, offset, drawLen);
  365.     
  366. } // MyDrawHook
  367.  
  368. #if GENERATINGCFM
  369.     static RoutineDescriptor gMyDrawGlueRD = BUILD_ROUTINE_DESCRIPTOR(uppDrawHookProcInfo, MyDrawHook);
  370.     static DrawHookUPP gMyDrawGlue = &gMyDrawGlueRD;
  371. #else
  372.     static DrawHookUPP gMyDrawGlue = NewDrawHookProc(MyDrawGlue);
  373. #endif
  374.  
  375. // --------------------------------------------------------------------------------------------------------------
  376. static void DisposeOfSpeech(Boolean throwAway)
  377. {
  378.     if (gSpeechChannel)
  379.         {
  380.         (void) StopSpeech( gSpeechChannel );
  381.         if (throwAway)
  382.             {
  383.             (void) DisposeSpeechChannel( gSpeechChannel );
  384.             gSpeechChannel = nil;
  385.             }
  386.         }
  387.     if (gSpeakPtr)
  388.         {
  389.         DisposePtr(gSpeakPtr);
  390.         gSpeakPtr = nil;
  391.         }
  392.         
  393. } // DisposeOfSpeech
  394.  
  395. // --------------------------------------------------------------------------------------------------------------
  396.  
  397. static Boolean FindNextPicture(Handle textHandle, short *offset, short *delta)
  398. {
  399.     long result;
  400.     
  401.     // marker at begining of document means a picture there
  402.     if ( (*offset == 0) && ( (*(unsigned char*)(*textHandle + (*offset))) == 0xCA) )
  403.         {
  404.         *delta = 1;
  405.         return true;
  406.         }
  407.  
  408.     if (gPictMarker1[0] != 0)
  409.         {
  410.         result = Munger(textHandle, *offset, &gPictMarker1[1], gPictMarker1[0], nil, 0);
  411.         if (result >= 0)
  412.             {
  413.             *offset = result;
  414.             *delta = gPictMarker1[0];
  415.             return true;
  416.             }
  417.         }
  418.  
  419.     if (gPictMarker2[0] != 0)
  420.         {
  421.         result = Munger(textHandle, *offset, &gPictMarker2[1], gPictMarker2[0], nil, 0);
  422.         if (result >= 0)
  423.             {
  424.             *offset = result;
  425.             *delta = gPictMarker2[0];
  426.             return true;
  427.             }
  428.         }
  429.         
  430.     *delta = 1;
  431.     return false;
  432.     
  433. } // FindNextPicture
  434.  
  435. // --------------------------------------------------------------------------------------------------------------
  436. static Boolean LineHasPageBreak(short lineNum, TEHandle hTE)
  437. {
  438.     Boolean        result = false;
  439.     short        offset, delta, lineStartOffset;
  440.     short        textLength;
  441.     Handle        textHandle;
  442.     short        pictIndex = 0;
  443.     
  444.     textHandle = (** hTE).hText;
  445.     lineStartOffset = (**hTE).lineStarts[lineNum];
  446.     textLength = (**hTE).lineStarts[lineNum+1];
  447.     
  448.     offset = 0;
  449.     while (offset < textLength)
  450.         {
  451.         if (FindNextPicture(textHandle, &offset, &delta))
  452.             {
  453.             Handle    formHandle;
  454.             
  455.             formHandle = Get1Resource(kFormResource, kFormResource + pictIndex);
  456.             if (formHandle)
  457.                 {
  458.                 long    options = **(long**)formHandle;
  459.                 
  460.                 ReleaseResource(formHandle);
  461.                 if ( (options & kFormFeed) && (offset >= lineStartOffset) && (offset < textLength) )
  462.                     {
  463.                     result = true;
  464.                     break;
  465.                     }
  466.                 }
  467.             ++pictIndex;
  468.             }
  469.         
  470.         offset += delta;
  471.         }
  472.         
  473.     return(result);
  474.     
  475. } // LineHasPageBreak
  476.  
  477. // --------------------------------------------------------------------------------------------------------------
  478. #define USE_PICT_SPOOL_CACHE 1
  479.  
  480. enum {kSpoolCacheBlockSize = 1024};        // 1K is arbitrary, perhaps could be tuned
  481.  
  482. static Handle    gSpoolPicture;
  483. static long        gSpoolOffset;
  484. static Handle    gSpoolCacheBlock;
  485. static long        gSpoolCacheOffset;
  486. static long        gSpoolCacheSize;
  487.  
  488. // --------------------------------------------------------------------------------------------------------------
  489. /*
  490.     ReadPartialResource is very painful over slow links such as ARA or ISDN. This code implements a simple
  491.     cache that vastly improves the performance of displaying embedded pictures over a slow network link.
  492.     The cache also improves scrolling performance in documents with many embedded pictures, even on local
  493.     volumes.
  494. */
  495. static short ReadPartialData(Ptr dataPtr, short byteCount)
  496. {
  497. #if USE_PICT_SPOOL_CACHE
  498.  
  499.     if (gSpoolCacheBlock == NULL)
  500.         {
  501.         gSpoolCacheBlock = NewHandle(kSpoolCacheBlockSize);
  502.         if (gSpoolCacheBlock != NULL)
  503.             {
  504.             long    cacheBytes    = GetResourceSizeOnDisk(gSpoolPicture) - gSpoolOffset;
  505.             
  506.             if (cacheBytes > kSpoolCacheBlockSize)
  507.                 cacheBytes = kSpoolCacheBlockSize;
  508.             
  509.             HLock(gSpoolCacheBlock);
  510.             ReadPartialResource(gSpoolPicture, gSpoolOffset, *gSpoolCacheBlock, cacheBytes);
  511.             HUnlock(gSpoolCacheBlock);
  512.             
  513.             gSpoolCacheOffset = gSpoolOffset;
  514.             gSpoolCacheSize = cacheBytes;
  515.             }
  516.         }
  517.     
  518.     // if the requested data is entirely present in the cache block, get it from there…
  519.     if (gSpoolCacheBlock != NULL &&
  520.         gSpoolOffset >= gSpoolCacheOffset && gSpoolOffset + byteCount <= gSpoolCacheOffset + gSpoolCacheSize)
  521.         {
  522.         BlockMoveData(*gSpoolCacheBlock + (gSpoolOffset - gSpoolCacheOffset), dataPtr, byteCount);
  523.         return byteCount;
  524.         }
  525.     // …else read it directly from disk, and force the cache block to be filled with new data
  526.     else
  527.         {
  528.         ReadPartialResource(gSpoolPicture, gSpoolOffset, dataPtr, byteCount);
  529.         if (gSpoolCacheBlock != NULL)
  530.             {
  531.             DisposeHandle(gSpoolCacheBlock);
  532.             gSpoolCacheBlock = NULL;
  533.             }
  534.         return byteCount;
  535.         }
  536.  
  537. #else
  538.  
  539.     ReadPartialResource(gSpoolPicture, gSpoolOffset, dataPtr, byteCount);
  540.     return byteCount;
  541.     
  542. #endif
  543.     
  544. } // ReadPartialData
  545.  
  546. // --------------------------------------------------------------------------------------------------------------
  547. static pascal void GetPartialPICTData(Ptr dataPtr,short byteCount)
  548. {
  549.     while (byteCount > 0)
  550.     {
  551.         short    readBytes    = ReadPartialData(dataPtr, byteCount);
  552.         
  553.         byteCount -= readBytes;
  554.         dataPtr += readBytes;
  555.         
  556.         gSpoolOffset += readBytes;
  557.     }
  558.     
  559. } // GetPartialPICTData
  560.  
  561. #if GENERATINGCFM
  562.     static RoutineDescriptor gGetPartialPICTDataRD = BUILD_ROUTINE_DESCRIPTOR(uppQDGetPicProcInfo, GetPartialPICTData);
  563.     static QDGetPicUPP gGetPartialPICTData = &gGetPartialPICTDataRD;
  564. #else
  565.     static QDGetPicUPP gGetPartialPICTData = NewQDGetPicProc(GetPartialPICTData);
  566. #endif
  567.  
  568. // --------------------------------------------------------------------------------------------------------------
  569. static void SpoolDrawPicture(Handle spoolPicture, PicHandle pictureHeader, Rect *pRect)
  570. {
  571.     CQDProcs    spoolProcs;
  572.     QDProcs        *oldProcs;
  573.  
  574.     gSpoolPicture = spoolPicture;
  575.     gSpoolOffset = sizeof(Picture);
  576.     
  577.     if (gMachineInfo.theEnvirons.hasColorQD)
  578.         SetStdCProcs(&spoolProcs);
  579.     else
  580.         SetStdProcs((QDProcs*) &spoolProcs);
  581.         
  582.     spoolProcs.getPicProc = gGetPartialPICTData;
  583.     oldProcs = qd.thePort->grafProcs;
  584.     qd.thePort->grafProcs = (QDProcs*) &spoolProcs;
  585.         DrawPicture(pictureHeader, pRect);
  586.     qd.thePort->grafProcs = oldProcs;
  587.     
  588.     if (gSpoolCacheBlock != NULL)
  589.         {
  590.         DisposeHandle(gSpoolCacheBlock);
  591.         gSpoolCacheBlock = NULL;
  592.         }
  593.     
  594. } // SpoolDrawPicture
  595.  
  596. // --------------------------------------------------------------------------------------------------------------
  597. static void DrawPictures( WindowDataPtr pData, TEHandle hTE)
  598. {
  599.     Handle    textHandle;
  600.     long    textLength;
  601.     short    oldResFile;
  602.     short    pictIndex;
  603.     short    numPicts;
  604.     
  605.     oldResFile = CurResFile();
  606.     UseResFile(pData->resRefNum);
  607.     
  608.     numPicts = Count1Resources('PICT');
  609.     pictIndex = 0;
  610.     
  611.     if (numPicts != 0)
  612.         {
  613.         short                offset, delta;
  614.         RgnHandle            oldClip;
  615.         Rect                theRect;
  616.         Rect                viewRect;
  617.         
  618.         viewRect = theRect = (** hTE).viewRect;
  619.         // intersect our viewing area with the actual clip to avoid
  620.         // drawing over the scroll bars
  621.             {
  622.             RgnHandle    newClip = NewRgn();
  623.             
  624.             oldClip = NewRgn();
  625.             GetClip(oldClip);
  626.             RectRgn(newClip, &theRect);
  627.             SectRgn(oldClip, newClip, newClip);
  628.             SetClip(newClip);
  629.             }
  630.         textHandle = (** hTE).hText;
  631.         textLength = (** hTE).teLength;
  632.         
  633.         offset = 0;
  634.         while (offset < textLength)
  635.             {
  636.             if (FindNextPicture(textHandle, &offset, &delta))
  637.                 {
  638.                 Handle        pictHandle;
  639.                 Point        picturePoint = TEGetPoint(offset, hTE);
  640.                 
  641.                 SetResLoad(false);
  642.                 pictHandle = Get1Resource('PICT', kPictureBase + pictIndex);
  643.                 SetResLoad(true);
  644.                 if (pictHandle)
  645.                     {
  646.                     PicHandle    pictHeader = (PicHandle)NewHandle(sizeof(Picture) + sizeof(long)*8);
  647.                     
  648.                     if (pictHeader)
  649.                         {
  650.                         HLock((Handle) pictHeader);
  651.                         ReadPartialResource(pictHandle, 0, (Ptr)*pictHeader, GetHandleSize((Handle)pictHeader));
  652.                         HUnlock((Handle) pictHeader);
  653.                         
  654.                         // calculate where to draw the picture, this location is
  655.                         // computed by:
  656.                         //  1) the frame of the original picture, normalized to 0,0
  657.                         //    2) the location of the non-breaking space character
  658.                         //    3) centering the picture on the content frame horizontally
  659.                         //    4) subtracting off the line-height of the character
  660.                         
  661.                         GetPICTRectangleAt72dpi(pictHeader, &theRect);
  662.                         OffsetRect(&theRect, -theRect.left, -theRect.top);
  663.                         OffsetRect(&theRect, 
  664.                                             theRect.left +
  665.                                             ((viewRect.right - viewRect.left) >> 1) -
  666.                                             ((theRect.right - theRect.left) >> 1),
  667.                                         picturePoint.v-theRect.top - pData->vScrollAmount);
  668.     
  669.                         // only draw the picture if it will be visible (vastly improves scrolling
  670.                         // performance in documents with many embedded pictures)
  671.                         
  672.                         if (RectInRgn(&theRect, qd.thePort->clipRgn))
  673.                             SpoolDrawPicture(pictHandle, pictHeader, &theRect);
  674.                         }
  675.                     ReleaseResource((Handle) pictHandle);
  676.                     }
  677.                 ++pictIndex;
  678.  
  679.                 offset += delta;
  680.                 }
  681.             else
  682.                 break;
  683.             }
  684.                 
  685.         SetClip(oldClip);
  686.         DisposeRgn(oldClip);
  687.         }
  688.         
  689.     UseResFile(oldResFile);
  690.     
  691. } // DrawPictures
  692.  
  693. // --------------------------------------------------------------------------------------------------------------
  694. static void UpdateFileInfo(FSSpec *pSpec, Boolean documentIsText)
  695. {
  696.     FInfo    theInfo;
  697.  
  698.     FSpGetFInfo(pSpec, &theInfo);
  699.     theInfo.fdCreator = 'ttxt';
  700.  
  701.     // set the stationary bit, if we must
  702.     if (!documentIsText)
  703.         {
  704.         theInfo.fdFlags |= kIsStationary;
  705.         theInfo.fdType = 'sEXT';
  706.         }
  707.     else
  708.         {
  709.         theInfo.fdFlags &= ~kIsStationary;
  710.         theInfo.fdType = 'TEXT';
  711.         }
  712.     FSpSetFInfo(pSpec, &theInfo);
  713.  
  714. } // UpdateFileInfo
  715.  
  716. // --------------------------------------------------------------------------------------------------------------
  717. static OSErr    TextSave(WindowDataPtr pData)
  718. {
  719.     OSErr    anErr = noErr;
  720.     long    amountToWrite;
  721.     
  722.     // write out the text
  723.     SetFPos(pData->dataRefNum, fsFromStart, 0);
  724.     amountToWrite = (** ((TextDataPtr) pData)->hTE).teLength;
  725.     anErr = FSWrite(pData->dataRefNum, &amountToWrite, * (** ((TextDataPtr) pData)->hTE).hText);
  726.     nrequire(anErr, FailedWrite);
  727.     SetEOF(pData->dataRefNum, amountToWrite);
  728.     
  729.     if (pData->resRefNum == -1)
  730.         {
  731.         FSpCreateResFile(&pData->fileSpec, 'ttxt', pData->originalFileType, 0);
  732.         pData->resRefNum = FSpOpenResFile(&pData->fileSpec, fsRdWrPerm);            
  733.         }
  734.     else
  735.         {
  736.         // a save always makes it into file of type 'TEXT', for Save As… we 
  737.         // afterwards set it again if the user is saving as stationary.
  738.         UpdateFileInfo(&pData->fileSpec, true);
  739.         }
  740.  
  741.     if (pData->resRefNum != -1)
  742.         {
  743.         short    oldResFile = CurResFile();
  744.         Handle    resourceHandle;
  745.         
  746.         UseResFile(pData->resRefNum);
  747.         
  748.         // remove any old sounds
  749.         resourceHandle = Get1Resource('snd ', kSoundBase);
  750.         if (resourceHandle)
  751.             {
  752.             RemoveResource(resourceHandle);
  753.             DisposeHandle(resourceHandle);
  754.             }
  755.             
  756.         // save the new sound
  757.         resourceHandle = ((TextDataPtr) pData)->soundHandle;
  758.         if (resourceHandle)
  759.             {
  760.             anErr = HandToHand(&resourceHandle);
  761.             if (anErr == noErr)
  762.                 {
  763.                 AddResource(resourceHandle, 'snd ', kSoundBase, "\p");
  764.                 anErr = ResError();
  765.                 }
  766.             nrequire(anErr, AddSoundResourceFailed);
  767.             }
  768.             
  769.         // remove any old styles
  770.         resourceHandle = Get1Resource('styl', 128);
  771.         if (resourceHandle)
  772.             {
  773.             RemoveResource(resourceHandle);
  774.             DisposeHandle(resourceHandle);
  775.             }
  776.         
  777.         // save the new style -- get the scrap handle from the TE record
  778.         // To do this, we must select the text -- BUT doing so through the
  779.         // TextEdit API results in lots of flashing and annoying behavior.
  780.         // So we just change the offsets by hand
  781.         {
  782.         short                oldStart, oldEnd;    
  783.         
  784.         oldStart = (** ((TextDataPtr) pData)->hTE).selStart;
  785.         oldEnd   = (** ((TextDataPtr) pData)->hTE).selEnd;
  786.         
  787.         (** ((TextDataPtr) pData)->hTE).selStart = 0;
  788.         (** ((TextDataPtr) pData)->hTE).selEnd = 32767;
  789.     
  790.         resourceHandle = (Handle) TEGetStyleScrapHandle( ((TextDataPtr) pData)->hTE );
  791.     
  792.         (** ((TextDataPtr) pData)->hTE).selStart = oldStart;
  793.         (** ((TextDataPtr) pData)->hTE).selEnd = oldEnd;
  794.         }
  795.     
  796.         if (resourceHandle)
  797.             {
  798.             AddResource(resourceHandle, 'styl', 128, "\p");
  799.             anErr = ResError();
  800.             nrequire(anErr, AddStyleResourceFailed);
  801.             }
  802.             
  803.         AddSoundResourceFailed:
  804.         AddStyleResourceFailed:    
  805.         
  806.             UpdateResFile(pData->resRefNum);
  807.             UseResFile(oldResFile);
  808.         }
  809.  
  810.         
  811. // FALL THROUGH EXCEPTION HANDLING
  812. FailedWrite:
  813.  
  814.     // if everything went okay, then clear the changed bit
  815.     if (anErr == noErr)
  816.         {
  817.         pData->changed = false;
  818.         (void) FlushVol("\p", pData->fileSpec.vRefNum);
  819.         }
  820.         
  821.     return anErr;
  822.     
  823. } // TextSave
  824.  
  825. // --------------------------------------------------------------------------------------------------------------
  826.  
  827. static pascal void DrawTextUserItem(DialogPtr dPtr, short theItem)
  828. /*
  829.     Draw text icon in the location
  830. */
  831. {
  832.     short        kind;
  833.     Handle        itemHandle;
  834.     Rect        box;
  835.     
  836.     GetDialogItem(dPtr, theItem, &kind, &itemHandle, &box);
  837.     PlotIconID(&box, ttNone, ttNone, kTextIcon);
  838.             
  839. } // DrawTextUserItem
  840.  
  841. #if GENERATINGCFM
  842.     static RoutineDescriptor gDrawTextUserItemRD = BUILD_ROUTINE_DESCRIPTOR(uppUserItemProcInfo, DrawTextUserItem);
  843.     static UserItemUPP gDrawTextUserItem = &gDrawTextUserItemRD;
  844. #else
  845.     static UserItemUPP gDrawTextUserItem = NewUserItemProc(DrawTextUserItem);
  846. #endif
  847.  
  848. // --------------------------------------------------------------------------------------------------------------
  849.  
  850. static pascal void DrawStationeryUserItem(DialogPtr dPtr, short theItem)
  851. /*
  852.     Draw stationery icon in the location
  853. */
  854. {
  855.     short        kind;
  856.     Handle        itemHandle;
  857.     Rect        box;
  858.     
  859.     GetDialogItem(dPtr, theItem, &kind, &itemHandle, &box);
  860.     PlotIconID(&box, ttNone, ttNone, kStationeryIcon);
  861.             
  862. } // DrawStationeryUserItem
  863.  
  864.  
  865. #if GENERATINGCFM
  866.     static RoutineDescriptor gDrawStationeryUserItemRD = BUILD_ROUTINE_DESCRIPTOR(uppUserItemProcInfo, DrawStationeryUserItem);
  867.     static UserItemUPP gDrawStationeryUserItem = &gDrawStationeryUserItemRD;
  868. #else
  869.     static UserItemUPP gDrawStationeryUserItem = NewUserItemProc(DrawStationeryUserItem);
  870. #endif
  871.  
  872.  
  873. // --------------------------------------------------------------------------------------------------------------
  874. // pre and post update procs for inline input 
  875.  
  876. static pascal void TSMPreUpdateProc(TEHandle textH, long refCon)
  877. {
  878. #pragma unused (refCon)
  879.  
  880.     ScriptCode     keyboardScript;
  881.     short        mode;
  882.     TextStyle    theStyle;
  883.  
  884.     keyboardScript = GetScriptManagerVariable(smKeyScript);
  885.     mode = doFont;
  886.     if     (!
  887.             (
  888.             (TEContinuousStyle(&mode, &theStyle, textH) )&& 
  889.             (FontToScript(theStyle.tsFont) == keyboardScript) 
  890.             )
  891.         )
  892.         {
  893.         theStyle.tsFont = GetScriptVariable(keyboardScript, smScriptAppFond);
  894.         TESetStyle(doFont, &theStyle, false, textH);
  895.         }
  896.         
  897. } // TSMPreUpdateProc
  898.  
  899. #if GENERATINGCFM
  900.     static RoutineDescriptor gTSMPreUpdateProcRD = BUILD_ROUTINE_DESCRIPTOR(uppTSMTEPreUpdateProcInfo, TSMPreUpdateProc);
  901.     static TSMTEPreUpdateUPP gTSMPreUpdateProc = &gTSMPreUpdateProcRD;
  902. #else
  903.     static TSMTEPreUpdateUPP gTSMPreUpdateProc = NewTSMTEPreUpdateProc(TSMPreUpdateProc);
  904. #endif
  905.  
  906. static pascal void TSMPostUpdateProc(
  907.                 TEHandle textH,
  908.                 long fixLen,
  909.                 long inputAreaStart,
  910.                 long inputAreaEnd,
  911.                 long pinStart,
  912.                 long pinEnd,
  913.                 long refCon)
  914. {
  915. #pragma unused (textH, fixLen, inputAreaStart, inputAreaEnd, pinStart, pinEnd)
  916.  
  917.     AdjustScrollBars((WindowRef)refCon, false, false, nil);
  918.     AdjustTE((WindowDataPtr)refCon, true);
  919.     
  920.     ((WindowDataPtr)refCon)->changed = true;
  921.     
  922. } // TSMPostUpdateProc
  923.  
  924. #if GENERATINGCFM
  925.     static RoutineDescriptor gTSMPostUpdateProcRD = BUILD_ROUTINE_DESCRIPTOR(uppTSMTEPostUpdateProcInfo, TSMPostUpdateProc);
  926.     static TSMTEPostUpdateUPP gTSMPostUpdateProc = &gTSMPostUpdateProcRD;
  927. #else
  928.     static TSMTEPostUpdateUPP gTSMPostUpdateProc = NewTSMTEPostUpdateProc(TSMPostUpdateProc);
  929. #endif
  930.  
  931. // --------------------------------------------------------------------------------------------------------------
  932.  
  933. static pascal short SaveDialogHook(short item, DialogPtr dPtr, Boolean *isText)
  934. {
  935.     short    theType;
  936.     Handle    theHandle;
  937.     Rect    theRect;
  938.     short    returnValue = item;
  939.     
  940.  
  941.     switch (item)
  942.         {
  943.         case sfHookFirstCall:
  944.             if (GetWRefCon(GetDialogWindow(dPtr)) == sfMainDialogRefCon)
  945.                 {
  946.                 GetDialogItem(dPtr, iTextUserItem, &theType, &theHandle, &theRect);
  947.                 theHandle = (Handle) gDrawTextUserItem;
  948.                 SetDialogItem(dPtr, iTextUserItem, theType, theHandle, &theRect);
  949.                 
  950.                 GetDialogItem(dPtr, iStationeryUserItem, &theType, &theHandle, &theRect);
  951.                 theHandle = (Handle) gDrawStationeryUserItem;
  952.                 SetDialogItem(dPtr, iStationeryUserItem, theType, theHandle, &theRect);
  953.     
  954.                 GetDialogItem(dPtr, iTextDocumentItem, &theType, &theHandle, &theRect);
  955.                 SetControlValue((ControlHandle) theHandle, 1);
  956.                 
  957.                 GetDialogItem(dPtr, iStationeryDocumentItem, &theType, &theHandle, &theRect);
  958.                 SetControlValue((ControlHandle) theHandle, 0);
  959.                 *isText = true;
  960.                 }
  961.             break;
  962.             
  963.         case iTextDocumentItem:
  964.         case iTextUserItem:
  965.             GetDialogItem(dPtr, iTextDocumentItem, &theType, &theHandle, &theRect);
  966.             SetControlValue((ControlHandle) theHandle, 1);
  967.             
  968.             GetDialogItem(dPtr, iStationeryDocumentItem, &theType, &theHandle, &theRect);
  969.             SetControlValue((ControlHandle) theHandle, 0);
  970.             
  971.             *isText = true;
  972.             returnValue = sfHookNullEvent;
  973.             break;
  974.             
  975.         case iStationeryDocumentItem:
  976.         case iStationeryUserItem:
  977.             GetDialogItem(dPtr, iTextDocumentItem, &theType, &theHandle, &theRect);
  978.             SetControlValue((ControlHandle) theHandle, 0);
  979.             
  980.             GetDialogItem(dPtr, iStationeryDocumentItem, &theType, &theHandle, &theRect);
  981.             SetControlValue((ControlHandle) theHandle, 1);
  982.             
  983.             *isText = false;
  984.             returnValue = sfHookNullEvent;
  985.             break;
  986.         
  987.         }
  988.         
  989.     return returnValue;
  990.     
  991. } // SaveDialogHook
  992.  
  993. #if GENERATINGCFM
  994.     static RoutineDescriptor gSaveDialogHookRD = BUILD_ROUTINE_DESCRIPTOR(uppDlgHookYDProcInfo, SaveDialogHook);
  995.     static DlgHookYDUPP gSaveDialogHook = &gSaveDialogHookRD;
  996. #else
  997.     static DlgHookYDUPP gSaveDialogHook = NewDlgHookYDProc(SaveDialogHook);
  998. #endif
  999.  
  1000. // --------------------------------------------------------------------------------------------------------------
  1001.  
  1002. // Handle update/activate events behind Standard File
  1003. static pascal Boolean SaveDialogFilter(DialogPtr theDialog, EventRecord *theEvent,
  1004.                                       short *itemHit, void *myDataPtr)
  1005. {
  1006.     #pragma unused(myDataPtr)
  1007.  
  1008.     if (StdFilterProc(theDialog, theEvent, itemHit))
  1009.         return true;
  1010.  
  1011.     // Pass updates through (Activates are tricky...was mucking with Apple menu & thereby
  1012.     // drastically changing how the system handles the menu bar during our alert)
  1013.     if (theEvent->what == updateEvt /* || theEvent->what == activateEvt */ )
  1014.         {
  1015.         HandleEvent(theEvent);
  1016.         }
  1017.  
  1018.     return false;
  1019.  
  1020. } // SaveDialogFilter
  1021.  
  1022.  
  1023. #if GENERATINGCFM
  1024.     static RoutineDescriptor gSaveDialogFilterRD = BUILD_ROUTINE_DESCRIPTOR(uppModalFilterYDProcInfo, SaveDialogFilter);
  1025.     static ModalFilterYDUPP gSaveDialogFilter = &gSaveDialogFilterRD;
  1026. #else
  1027.     static ModalFilterYDUPP gSaveDialogFilter = NewModalFilterYDProc(SaveDialogFilter);
  1028. #endif
  1029.  
  1030. // --------------------------------------------------------------------------------------------------------------
  1031.  
  1032. static OSErr    TextSaveAs(WindowRef pWindow, WindowDataPtr pData)
  1033. {
  1034.     OSErr                anErr = noErr;
  1035.     short                oldRes, oldData;
  1036.     Boolean                documentIsText;
  1037.     Boolean                replacing;
  1038.     FSSpec                fileSpec;
  1039.     NavReplyRecord        navReply;
  1040.     
  1041.     // save the old references -- if there is an error, we restore them
  1042.     oldRes = pData->resRefNum;
  1043.     oldData = pData->dataRefNum;
  1044.     
  1045.     // ask where and how to save this document
  1046.     {
  1047.     Str255    defaultName;
  1048.     Point    where = {-1, -1};
  1049.     
  1050.     // setup for the call
  1051.     GetWTitle(pWindow, defaultName);
  1052.     SetCursor(&qd.arrow);
  1053.     
  1054.     // find out where the user wants the file
  1055.     if (gMachineInfo.haveNavigationServices)
  1056.     {
  1057.         Boolean stationery;
  1058.  
  1059.         anErr = SaveFileDialog(defaultName, pData->originalFileType, 'ttxt', MyEventProc, &fileSpec, &stationery, &replacing, &navReply);
  1060.         if ( anErr == userCanceledErr)
  1061.             anErr = eUserCanceled;
  1062.             
  1063.         documentIsText = stationery == false;
  1064.     }
  1065.     else
  1066.     {
  1067.         StandardFileReply    sfReply;
  1068.         
  1069.         CustomPutFile("\p", defaultName, &sfReply, 
  1070.                     kTextSaveAsDialogID, where,
  1071.                     gSaveDialogHook, gSaveDialogFilter, nil, nil, &documentIsText);
  1072.                     
  1073.         // map the cancel button into a cancelling error
  1074.         if (!sfReply.sfGood)
  1075.             anErr = eUserCanceled;
  1076.  
  1077.         replacing    = sfReply.sfReplacing;
  1078.         fileSpec    = sfReply.sfFile;
  1079.     }
  1080.     
  1081.     }
  1082.     // can't replace over other types    
  1083.     if (replacing)
  1084.         {
  1085.         FInfo    theInfo;
  1086.         
  1087.         FSpGetFInfo(&fileSpec, &theInfo);
  1088.         
  1089.         if ( (theInfo.fdType != 'TEXT') && (theInfo.fdType != 'sEXT') )
  1090.             anErr = eDocumentWrongKind;
  1091.         }
  1092.     nrequire(anErr, StandardPutFile);
  1093.         
  1094.     if     (
  1095.         (replacing) && 
  1096.         (oldData != -1) && 
  1097.         (pData->fileSpec.vRefNum == fileSpec.vRefNum) &&
  1098.         (pData->fileSpec.parID == fileSpec.parID) &&
  1099.         EqualString(pData->fileSpec.name, fileSpec.name, false, false)
  1100.         )
  1101.         {
  1102.  
  1103.         anErr = TextSave(pData);
  1104.         
  1105.         if (anErr == noErr)
  1106.             UpdateFileInfo(&fileSpec, documentIsText);
  1107.         }
  1108.     else
  1109.         {
  1110.         // create the data file and resource fork
  1111.         (void) FSpDelete(&fileSpec);
  1112.         anErr = FSpCreate(&fileSpec, 'ttxt', documentIsText ? 'TEXT' : 'sEXT', 0);
  1113.         FSpCreateResFile(&fileSpec, 'ttxt', documentIsText ? 'TEXT' : 'sEXT', 0);
  1114.         nrequire(anErr, FailedCreate);
  1115.  
  1116.         // set the stationary bit, if we must
  1117.         if (!documentIsText)
  1118.             {
  1119.             FInfo    theInfo;
  1120.             
  1121.             FSpGetFInfo(&fileSpec, &theInfo);
  1122.             theInfo.fdFlags |= kIsStationary;
  1123.             FSpSetFInfo(&fileSpec, &theInfo);
  1124.             }
  1125.  
  1126.         // open both of forks
  1127.         anErr = FSpOpenDF(&fileSpec, fsRdWrPerm, &pData->dataRefNum);
  1128.         if (anErr == noErr)
  1129.             {
  1130.             pData->resRefNum = FSpOpenResFile(&fileSpec, fsRdWrPerm);
  1131.             anErr = ResError();
  1132.             }
  1133.         else
  1134.             FSpDelete(&fileSpec);
  1135.  
  1136.         // call the standard save function to do the save
  1137.         anErr = TextSave(pData);
  1138.     
  1139.     // FALL THROUGH EXCEPTION HANDLING    
  1140.     FailedCreate:
  1141.     StandardPutFile:
  1142.  
  1143.         // finally, close the old files if everything went okay
  1144.         if (anErr == noErr)
  1145.             {
  1146.             if (oldRes != -1)
  1147.                 CloseResFile(oldRes);
  1148.             if (oldData != -1)
  1149.                 FSClose(oldData);
  1150.             pData->isWritable = true;
  1151.             SetWTitle(pWindow, fileSpec.name);
  1152.             }
  1153.         else
  1154.             {
  1155.             pData->resRefNum = oldRes;
  1156.             pData->dataRefNum = oldData;
  1157.             }
  1158.         }
  1159.     
  1160.     // save new location
  1161.     if (anErr == noErr)
  1162.         BlockMoveData(&fileSpec, &pData->fileSpec, sizeof(FSSpec));
  1163.         
  1164. //•• Return eUserCanceled so we can avoid closing/quitting if they cancel the SF dialog
  1165. //    // don't propagate this error
  1166. //    if (anErr == eUserCanceled)
  1167. //        anErr = noErr;
  1168.  
  1169.  
  1170.     if (gMachineInfo.haveNavigationServices)
  1171.     {
  1172.         // file must be closed in order to 'translate in place', so close both forks:
  1173.         if (pData->resRefNum != -1)
  1174.             CloseResFile(pData->resRefNum);        
  1175.         if (pData->dataRefNum != -1)
  1176.             FSClose(pData->dataRefNum);
  1177.             
  1178.         if (anErr == noErr)
  1179.             {
  1180.             anErr = CompleteSave(&fileSpec, &navReply);    // do the translation (in place)
  1181.             
  1182.             // reopen both forks:
  1183.             anErr = FSpOpenDF(&fileSpec, fsRdWrPerm, &pData->dataRefNum);
  1184.             if (anErr == noErr)
  1185.                 {
  1186.                 pData->resRefNum = FSpOpenResFile(&fileSpec, fsRdWrPerm);
  1187.                 anErr = ResError();
  1188.                 }
  1189.             
  1190.             // •• problem:
  1191.             // Save As… model fails in this case because we still have the old window open,
  1192.             // Should SimpleText close the current text window and open a new one with the translated data?
  1193.             }
  1194.     }
  1195.     
  1196.     return anErr;
  1197.     
  1198. } // TextSaveAs
  1199.  
  1200. // --------------------------------------------------------------------------------------------------------------
  1201. static void ApplyFace(short requestedFace, WindowRef pWindow, WindowDataPtr pData, short commandID)
  1202. {
  1203.     TextStyle    style;
  1204.     short        oldNumLines = (**(((TextDataPtr) pData)->hTE)).nLines;
  1205.     
  1206.     SaveCurrentUndoState(pData, commandID);
  1207.     
  1208.     style.tsFace = requestedFace;
  1209.     TESetStyle(doFace + ((requestedFace != normal) ? doToggle : 0), &style, true, ((TextDataPtr) pData)->hTE);
  1210.     TECalText(((TextDataPtr) pData)->hTE);
  1211.     
  1212.     oldNumLines -= (**(((TextDataPtr) pData)->hTE)).nLines;
  1213.     AdjustTE(pData, false);
  1214.     AdjustScrollBars(pWindow, (oldNumLines > 0), (oldNumLines > 0), nil);
  1215.  
  1216.     pData->changed = true;
  1217.     
  1218. } // ApplyFace
  1219.  
  1220. // --------------------------------------------------------------------------------------------------------------
  1221. static OSErr ApplySize(short requestedSize, WindowRef pWindow, WindowDataPtr pData, short commandID)
  1222. {
  1223.     OSErr        anErr = noErr;
  1224.     TextStyle    style;
  1225.     short        oldNumLines = (**(((TextDataPtr) pData)->hTE)).nLines;
  1226.     
  1227.     SaveCurrentUndoState(pData, commandID);
  1228.  
  1229.     style.tsSize = requestedSize;
  1230.     TESetStyle(doSize, &style, true, ((TextDataPtr) pData)->hTE);
  1231.     TECalText(((TextDataPtr) pData)->hTE);
  1232.     if (CalculateTextEditHeight(((TextDataPtr) pData)->hTE) > 32767)
  1233.         {        
  1234.         style.tsSize = 0;
  1235.         TESetStyle(doSize, &style, true, ((TextDataPtr) pData)->hTE);
  1236.         anErr = eDocumentTooLarge;
  1237.         }
  1238.  
  1239.     oldNumLines -= (**(((TextDataPtr) pData)->hTE)).nLines;
  1240.     AdjustTE(pData, false);
  1241.     AdjustScrollBars(pWindow, (oldNumLines > 0), (oldNumLines > 0), nil);
  1242.  
  1243.     pData->changed = true;
  1244.     
  1245.     return anErr;
  1246.  
  1247. } // ApplySize
  1248.  
  1249. // --------------------------------------------------------------------------------------------------------------
  1250. // OOP INTERFACE ROUTINES
  1251. // --------------------------------------------------------------------------------------------------------------
  1252.  
  1253. static OSErr    TextUpdateWindow(WindowRef pWindow, WindowDataPtr pData)
  1254. {
  1255.     Rect    updateArea = pData->contentRect;
  1256.     
  1257.     // be sure to also erase the area where the horizontal scroll bar
  1258.     // is missing.
  1259.     updateArea.bottom = GetWindowPort(pWindow)->portRect.bottom;
  1260.     EraseRect(&updateArea);
  1261.     
  1262.     TEUpdate(&pData->contentRect, ((TextDataPtr) pData)->hTE);
  1263.     
  1264.     DrawPictures(pData, ((TextDataPtr) pData)->hTE);
  1265.     
  1266.     DrawControls(pWindow);
  1267.     DrawGrowIcon(pWindow);
  1268.     
  1269.     return noErr;
  1270.     
  1271. } // TextUpdateWindow
  1272.  
  1273. // --------------------------------------------------------------------------------------------------------------
  1274.  
  1275. static OSErr    TextCloseWindow(WindowRef pWindow, WindowDataPtr pData)
  1276. {
  1277. #pragma unused (pWindow)
  1278.  
  1279.     // shut down text services
  1280.     if (pData->docTSMDoc)
  1281.         {
  1282.         FixTSMDocument(pData->docTSMDoc);
  1283.         DeactivateTSMDocument(pData->docTSMDoc);
  1284.         DeleteTSMDocument(pData->docTSMDoc);
  1285.         }
  1286.     
  1287.     DisposeOfSpeech(true);
  1288.     DisposeHandle(((TextDataPtr) pData)->soundHandle);
  1289.     TEDispose(((TextDataPtr) pData)->hTE);
  1290.  
  1291.     DisposeHandle(((TextDataPtr) pData)->prevText);
  1292.     DisposeHandle(((TextDataPtr) pData)->prevStyle);
  1293.     
  1294.     TextRemoveContentsMenu(pData);
  1295.  
  1296.     return noErr;
  1297.  
  1298. } // TextCloseWindow
  1299.  
  1300. // --------------------------------------------------------------------------------------------------------------
  1301.  
  1302. static OSErr    TextActivateEvent(WindowRef pWindow, WindowDataPtr pData, Boolean activating)
  1303. {
  1304. #pragma unused (pWindow)
  1305.  
  1306.     // only modifyable docs can be active
  1307.     if (pData->originalFileType == 'TEXT')
  1308.         {
  1309.         if (activating)
  1310.             {
  1311.             TEActivate(((TextDataPtr) pData)->hTE);
  1312.             if (pData->docTSMDoc != nil)
  1313.                 ActivateTSMDocument(pData->docTSMDoc);
  1314.             }
  1315.         else
  1316.             {
  1317.             TEDeactivate(((TextDataPtr) pData)->hTE);
  1318.             if (pData->docTSMDoc != nil)
  1319.                 DeactivateTSMDocument(pData->docTSMDoc);
  1320.             }
  1321.         }
  1322.         
  1323.     // add contents menu, if appropriate        
  1324.     if (activating)
  1325.         {
  1326.         TextAddContentsMenu(pData);
  1327.         }
  1328.     else
  1329.         {
  1330.         TextRemoveContentsMenu(pData);
  1331.         }
  1332.     
  1333.     return noErr;
  1334.     
  1335. } // TextActivateEvent
  1336.  
  1337. // --------------------------------------------------------------------------------------------------------------
  1338.  
  1339. static Boolean    TextFilterEvent(WindowRef pWindow, WindowDataPtr pData, EventRecord *pEvent)
  1340. {    
  1341.     switch (pEvent->what)
  1342.         {
  1343.         case nullEvent:
  1344.             if (pData->originalFileType == 'TEXT')
  1345.                 {
  1346.                 if ( pWindow == FrontWindow() )
  1347.                     TEIdle(((TextDataPtr) pData)->hTE);
  1348.                 }
  1349.                 
  1350.             // if we stop speaking, ditch the channel
  1351.             if (gSpeechChannel) 
  1352.                 {
  1353.                 SpeechStatusInfo    status;                // Status of our speech channel.
  1354.                 
  1355.                 if ( 
  1356.                     (GetSpeechInfo( gSpeechChannel, soStatus, (void*) &status ) == noErr)
  1357.                     &&  (!status.outputBusy )
  1358.                     )
  1359.                     DisposeOfSpeech(true);
  1360.                 }
  1361.             break;
  1362.         }
  1363.         
  1364.     return false;
  1365.     
  1366. } // TextFilterEvent
  1367.  
  1368. // --------------------------------------------------------------------------------------------------------------
  1369.  
  1370. static OSErr    TextScrollContent(WindowRef pWindow, WindowDataPtr pData, short deltaH, short deltaV)
  1371. {
  1372.     GrafPtr        port = (GrafPtr) GetWindowPort(pWindow);
  1373.     RgnHandle    srcRgn, dstRgn;
  1374.     Rect        viewRect;
  1375.     
  1376.     // scroll the text area
  1377.     TEScroll(deltaH, deltaV, ((TextDataPtr) pData)->hTE);
  1378.  
  1379.     // calculate the region that is uncovered by the scroll
  1380.     srcRgn = NewRgn();
  1381.     dstRgn = NewRgn();
  1382.     viewRect = (**((TextDataPtr) pData)->hTE).viewRect;
  1383.     RectRgn( srcRgn, &viewRect );
  1384.     SectRgn( srcRgn, port->visRgn,  srcRgn );
  1385.     SectRgn( srcRgn, port->clipRgn, srcRgn );
  1386.     CopyRgn( srcRgn, dstRgn );
  1387.     OffsetRgn( dstRgn, deltaH, deltaV );
  1388.     SectRgn( srcRgn, dstRgn, dstRgn );
  1389.     DiffRgn( srcRgn, dstRgn, srcRgn );
  1390.  
  1391.     // clip to this new area
  1392.     GetClip(dstRgn);
  1393.     SetClip(srcRgn);
  1394.         DrawPictures(pData, ((TextDataPtr) pData)->hTE);
  1395.     SetClip(dstRgn);
  1396.     
  1397.     // all done with these calculation regions
  1398.     DisposeRgn( srcRgn );
  1399.     DisposeRgn( dstRgn );
  1400.     
  1401.     return eActionAlreadyHandled;
  1402.     
  1403. } // TextScrollContent
  1404.  
  1405. // --------------------------------------------------------------------------------------------------------------
  1406.  
  1407. static OSErr    TextKeyEvent(WindowRef pWindow, WindowDataPtr pData, EventRecord *pEvent, Boolean isMotionKey)
  1408. {    
  1409.     OSErr    anErr = noErr;
  1410.     
  1411.     if (!(pEvent->modifiers & cmdKey)) 
  1412.         {
  1413.         char    theKey = pEvent->message & charCodeMask;
  1414.         char    theKeyCode = (pEvent->message >> 8) & charCodeMask;
  1415.         
  1416.         if ( ((theKey != kDeleteKey) || (theKeyCode != kForwardDeleteKey)) && 
  1417.             ((** ((TextDataPtr) pData)->hTE).teLength+1 > kMaxLength) )
  1418.             anErr = eDocumentTooLarge;
  1419.         else
  1420.             {
  1421.             long        oldHeight = CalculateTextEditHeight(((TextDataPtr) pData)->hTE);
  1422.             long        end = (**(((TextDataPtr) pData)->hTE)).selEnd;
  1423.             long        start = (**(((TextDataPtr) pData)->hTE)).selStart;
  1424.  
  1425.             ObscureCursor();
  1426.             SaveCurrentUndoState(pData, cTypingCommand);
  1427.             if (theKeyCode != kForwardDeleteKey)
  1428.                 {
  1429.                 if (pEvent->modifiers & shiftKey)
  1430.                     {
  1431.                     switch (theKeyCode)
  1432.                         {
  1433.                         case kUpArrow:
  1434.                             TEKey(theKey, ((TextDataPtr) pData)->hTE);
  1435.                             TESetSelect((**(((TextDataPtr) pData)->hTE)).selStart, end, ((TextDataPtr) pData)->hTE);
  1436.                             break;
  1437.                             
  1438.                         case kDownArrow:
  1439.                             TESetSelect(end, end, ((TextDataPtr) pData)->hTE);
  1440.                             TEKey(theKey, ((TextDataPtr) pData)->hTE);
  1441.                             TESetSelect(start, (**(((TextDataPtr) pData)->hTE)).selEnd, ((TextDataPtr) pData)->hTE);
  1442.                             break;
  1443.                             
  1444.                         case kRightArrow:
  1445.                             {
  1446.                             Handle textHandle = (**(((TextDataPtr) pData)->hTE)).hText;
  1447.                             Ptr textBuf;
  1448.                             char state;
  1449.                             
  1450.                             state = HGetState(textHandle);
  1451.                             HLock(textHandle);
  1452.                             textBuf = *(**(((TextDataPtr) pData)->hTE)).hText;
  1453.                             if (CharacterByteType(textBuf, start, smCurrentScript) != smSingleByte)
  1454.                                 ++end;
  1455.                             HSetState(textHandle, state);
  1456.                             TESetSelect(start, ++end, ((TextDataPtr) pData)->hTE);
  1457.                             }
  1458.                             break;
  1459.                             
  1460.                         case kLeftArrow:
  1461.                             if (start > 0)
  1462.                                 {
  1463.                                 if (start > 1)
  1464.                                     {
  1465.                                     Handle textHandle = (**(((TextDataPtr) pData)->hTE)).hText;
  1466.                                     Ptr textBuf;
  1467.                                     char state;
  1468.                                     
  1469.                                     state = HGetState(textHandle);
  1470.                                     HLock(textHandle);
  1471.                                     textBuf = *(**(((TextDataPtr) pData)->hTE)).hText;
  1472.                                     if (CharacterByteType(textBuf, start-1, smCurrentScript) != smSingleByte)
  1473.                                         --start;
  1474.                                     HSetState(textHandle, state);
  1475.                                     }
  1476.                                 TESetSelect(--start, end, ((TextDataPtr) pData)->hTE);
  1477.                                 }
  1478.                             break;
  1479.                             
  1480.                         default:
  1481.                             TEKey(theKey, ((TextDataPtr) pData)->hTE);
  1482.                         }
  1483.                     }
  1484.                 else
  1485.                     TEKey(theKey, ((TextDataPtr) pData)->hTE);
  1486.                 }
  1487.             else
  1488.                 {
  1489.                 if     (end < (**(((TextDataPtr) pData)->hTE)).teLength)
  1490.                     {
  1491.                     if (start == end)
  1492.                         TEKey(kRightArrowCharCode, ((TextDataPtr) pData)->hTE);
  1493.                     TEKey(kBackspaceCharCode, ((TextDataPtr) pData)->hTE);
  1494.                     }
  1495.                 }
  1496.             oldHeight -= CalculateTextEditHeight(((TextDataPtr) pData)->hTE);
  1497.                             
  1498.             ((TextDataPtr) pWindow)->insideClickLoop = true;
  1499.                 AdjustTE(pData, false);
  1500.                 AdjustScrollBars(pWindow, (oldHeight > 0), (oldHeight > 0), nil);
  1501.             ((TextDataPtr) pWindow)->insideClickLoop = false;
  1502.  
  1503.             if (!isMotionKey)
  1504.                 pData->changed = true;
  1505.             }
  1506.         }
  1507.  
  1508.     return anErr;
  1509.  
  1510. } // TextKeyEvent
  1511.  
  1512. // --------------------------------------------------------------------------------------------------------------
  1513.  
  1514. static OSErr    TextContentClick(WindowRef pWindow, WindowDataPtr pData, EventRecord *pEvent)
  1515. {
  1516.     OSErr            anErr = noErr;
  1517.     Point            clickPoint = pEvent->where;
  1518.     ControlHandle    theControl;
  1519.     RgnHandle        hilightRgn;
  1520.  
  1521.     GlobalToLocal(&clickPoint);
  1522.     if (FindControl(clickPoint, pWindow, &theControl) == 0)
  1523.         {
  1524.         if (gMachineInfo.haveDragMgr)
  1525.             {
  1526.             hilightRgn = NewRgn();
  1527.             TEGetHiliteRgn(hilightRgn, ((TextDataPtr) pData)->hTE);
  1528.     
  1529.             if (PtInRgn(clickPoint, hilightRgn))
  1530.                 {
  1531.                 SaveCurrentUndoState(pData, cTypingCommand);
  1532.                 if (!DragText(pWindow, pData, pEvent, hilightRgn))
  1533.                     anErr = eActionAlreadyHandled;
  1534.                 }
  1535.             else        
  1536.                 {
  1537.                 anErr = eActionAlreadyHandled;
  1538.                 }
  1539.     
  1540.             DisposeRgn(hilightRgn);
  1541.             }
  1542.         else
  1543.             {
  1544.             anErr = eActionAlreadyHandled;
  1545.             }
  1546.         }
  1547.  
  1548.     if ( (anErr == eActionAlreadyHandled) && (PtInRect(clickPoint, &pData->contentRect)) )
  1549.         {
  1550.         TEClick(clickPoint, (pEvent->modifiers & shiftKey) != 0, ((TextDataPtr) pData)->hTE);
  1551.         }
  1552.         
  1553.     return anErr;
  1554.     
  1555. } // TextContentClick
  1556.  
  1557. // --------------------------------------------------------------------------------------------------------------
  1558.  
  1559. static OSErr    TextAdjustSize(WindowRef pWindow, WindowDataPtr pData, 
  1560.             Boolean *didReSize) // input: was window resized, output: t->we resized something
  1561. {
  1562. #pragma unused (pWindow)
  1563.     
  1564.     if (*didReSize)
  1565.         {
  1566.         RecalcTE(pData, true);
  1567.         AdjustTE(pData, true);
  1568.         }
  1569.     else
  1570.         {
  1571.         AdjustTE(pData, false);
  1572.         }
  1573.         
  1574.     return noErr;
  1575.     
  1576. } // TextAdjustSize
  1577.  
  1578. // --------------------------------------------------------------------------------------------------------------
  1579.  
  1580. static short FontToQD(gxFont fontID, Style* styleBits)
  1581. {
  1582.     short            numFonts = GetHandleSize((Handle) gFontMappingList) / sizeof(FontMappingRecord);
  1583.     short            i;
  1584.     FontMappingPtr    pList = *gFontMappingList;
  1585.     
  1586.     for (i = 0; i < numFonts; ++i)
  1587.         {
  1588.         if (pList->fontID == fontID)
  1589.             {
  1590.             if (styleBits)
  1591.                 *styleBits = pList->qdStyle;
  1592.             return(pList->qdFont);
  1593.             }
  1594.         pList++;
  1595.         }
  1596.     
  1597.     // error case?   default.
  1598.     if (styleBits)
  1599.         *styleBits = normal;
  1600.     return(0);
  1601.     
  1602. } // FontToQD
  1603.  
  1604. // --------------------------------------------------------------------------------------------------------------
  1605.  
  1606. static gxFont QDToFont(short qdFont, Style styleBits)
  1607. {
  1608.     short            numFonts = GetHandleSize((Handle) gFontMappingList) / sizeof(FontMappingRecord);
  1609.     short            i;
  1610.     FontMappingPtr    pList;
  1611.     
  1612.     if (qdFont == systemFont)
  1613.         qdFont = GetSysFont();
  1614.     if (qdFont == applFont)
  1615.         qdFont = GetAppFont();
  1616.  
  1617.     // try to match both font and style
  1618.     pList = *gFontMappingList;
  1619.     for (i = 0; i < numFonts; ++i)
  1620.         {
  1621.         if ((pList->qdFont == qdFont) && (styleBits == pList->qdStyle))
  1622.             return(pList->fontID);
  1623.         pList++;
  1624.         }
  1625.     
  1626.     // if we can't match font and style, just look for font
  1627.     pList = *gFontMappingList;
  1628.     for (i = 0; i < numFonts; ++i)
  1629.         {
  1630.         if (pList->qdFont == qdFont)
  1631.             return(pList->fontID);
  1632.         pList++;
  1633.         }
  1634.     
  1635.     // error case?   default.
  1636.     return(nil);
  1637.     
  1638. } // QDToFont
  1639.  
  1640. // --------------------------------------------------------------------------------------------------------------
  1641.  
  1642. static OSErr    TextCommand(WindowRef pWindow, WindowDataPtr pData, short commandID, long menuResult)
  1643. {
  1644.  
  1645.     OSErr    anErr = noErr;
  1646.     
  1647.     SetPort((GrafPtr) GetWindowPort(pWindow));
  1648.     
  1649.     if ( (pData->docTSMDoc) && (menuResult != 0) )
  1650.         FixTSMDocument(pData->docTSMDoc);
  1651.         
  1652.     switch (commandID)
  1653.         {            
  1654.         case cUndo:
  1655.             {
  1656.             short        oldNumLines = (**(((TextDataPtr) pData)->hTE)).nLines;
  1657.  
  1658.             PerformUndo(pData);
  1659.             oldNumLines -= (**(((TextDataPtr) pData)->hTE)).nLines;
  1660.             AdjustTE(pData, false);
  1661.             AdjustScrollBars(pWindow, (oldNumLines > 0), (oldNumLines > 0), nil);
  1662.             RecalcTE(pData, true);
  1663.             pData->changed = true;
  1664.             }
  1665.             break;
  1666.             
  1667.         case cCut:
  1668.             SaveCurrentUndoState(pData, cCut);
  1669.             {
  1670.             short        oldNumLines = (**(((TextDataPtr) pData)->hTE)).nLines;
  1671.  
  1672.             TECut(((TextDataPtr) pData)->hTE);    // no need for TEToScrap with styled TE record
  1673.             oldNumLines -= (**(((TextDataPtr) pData)->hTE)).nLines;
  1674.             AdjustTE(pData, false);
  1675.             AdjustScrollBars(pWindow, (oldNumLines > 0), (oldNumLines > 0), nil);
  1676.             pData->changed = true;
  1677.             }
  1678.             break;
  1679.             
  1680.         case cCopy:
  1681.             TECopy(((TextDataPtr) pData)->hTE);    // no need for TEToScrap with styled TE record
  1682.             AdjustTE(pData, false);
  1683.             break;
  1684.             
  1685.         case cClear:
  1686.             SaveCurrentUndoState(pData, cClear);
  1687.             {
  1688.             short        oldNumLines = (**(((TextDataPtr) pData)->hTE)).nLines;
  1689.  
  1690.             TEDelete(((TextDataPtr) pData)->hTE);
  1691.             oldNumLines -= (**(((TextDataPtr) pData)->hTE)).nLines;
  1692.             AdjustTE(pData, false);
  1693.             AdjustScrollBars(pWindow, (oldNumLines > 0), (oldNumLines > 0), nil);
  1694.             pData->changed = true;
  1695.             }
  1696.             break;
  1697.             
  1698.         case cPaste:
  1699.             SaveCurrentUndoState(pData, cPaste);
  1700.             {
  1701.             short        oldNumLines = (**(((TextDataPtr) pData)->hTE)).nLines;
  1702.  
  1703.             anErr = TEFromScrap();            
  1704.             if (anErr == noErr)
  1705.                 {                
  1706.                 // if the current length, plus the paste data, minus the data in the selection
  1707.                 // would make the document too large, say so
  1708.                 if     ( 
  1709.                         ((** ((TextDataPtr) pData)->hTE).teLength +
  1710.                         TEGetScrapLength() -
  1711.                         ((** ((TextDataPtr) pData)->hTE).selEnd-(** ((TextDataPtr) pData)->hTE).selStart)
  1712.                         )
  1713.                     > kMaxLength)
  1714.                     {
  1715.                     anErr = eDocumentTooLarge;
  1716.                     }
  1717.                 else
  1718.                     {
  1719.                     Handle    aHandle = (Handle) TEGetText(((TextDataPtr) pData)->hTE);
  1720.                     Size    oldSize = GetHandleSize(aHandle);
  1721.                     Size    newSize = oldSize + TEGetScrapLength();
  1722.                     OSErr    saveErr;
  1723.                     
  1724.                     SetHandleSize(aHandle, newSize);
  1725.                     saveErr = MemError();
  1726.                     SetHandleSize(aHandle, oldSize);
  1727.                     if (saveErr != noErr)
  1728.                         anErr = eDocumentTooLarge;
  1729.                     else
  1730.                         TEStylePaste(((TextDataPtr) pData)->hTE);
  1731.                     }
  1732.                     
  1733.                 UnloadScrap();
  1734.                 }
  1735.             oldNumLines -= (**(((TextDataPtr) pData)->hTE)).nLines;
  1736.             AdjustTE(pData, false);
  1737.             AdjustScrollBars(pWindow, (oldNumLines > 0), (oldNumLines > 0), nil);
  1738.             pData->changed = true;
  1739.             }
  1740.             break;
  1741.             
  1742.         case cReplace:
  1743.             SaveCurrentUndoState(pData, cReplace);
  1744.             {
  1745.             short result = ConductFindOrReplaceDialog(kReplaceWindowID);
  1746.             
  1747.             if (result == cancel)
  1748.                 break;
  1749.                 
  1750.             if (result == iReplaceAll)
  1751.                 {
  1752.                 long         newStart, newEnd;
  1753.                 short        oldNumLines = (**(((TextDataPtr) pData)->hTE)).nLines;
  1754.                 
  1755.                 TESetSelect(0, 0, ((TextDataPtr) pData)->hTE);
  1756.                 while (PerformSearch((**((TextDataPtr) pData)->hTE).hText,
  1757.                                     (**((TextDataPtr) pData)->hTE).selStart,
  1758.                                     gFindString, gCaseSensitive, false, false,
  1759.                                     &newStart, &newEnd))
  1760.                     {
  1761.                     TESetSelect(newStart, newEnd, ((TextDataPtr) pData)->hTE);
  1762.                     TEDelete(((TextDataPtr) pData)->hTE);
  1763.                     TEInsert(&gReplaceString[1], gReplaceString[0], ((TextDataPtr) pData)->hTE);
  1764.                     TESetSelect(newStart, newStart+gReplaceString[0], ((TextDataPtr) pData)->hTE);                
  1765.                     pData->changed = true;
  1766.                     }
  1767.                 oldNumLines -= (**(((TextDataPtr) pData)->hTE)).nLines;
  1768.                 
  1769.                 AdjustTE(pData, false);
  1770.                 AdjustScrollBars(pWindow, (oldNumLines > 0), (oldNumLines > 0), nil);
  1771.     
  1772.                 anErr = eActionAlreadyHandled;
  1773.                 break;
  1774.                 }
  1775.             }
  1776.         
  1777.         // fall through from replace
  1778.         case cReplaceAgain:
  1779.             SaveCurrentUndoState(pData, cReplaceAgain);
  1780.             {
  1781.             long     newStart, newEnd;
  1782.             Boolean    isBackwards = ((gEvent.modifiers & shiftKey) != 0);
  1783.             
  1784.             if (PerformSearch((**((TextDataPtr) pData)->hTE).hText,
  1785.                                 isBackwards ? (**((TextDataPtr) pData)->hTE).selEnd : (**((TextDataPtr) pData)->hTE).selStart,
  1786.                                 gFindString, gCaseSensitive, isBackwards, gWrapAround,
  1787.                                 &newStart, &newEnd))
  1788.                 {
  1789.                 short        oldNumLines = (**(((TextDataPtr) pData)->hTE)).nLines;
  1790.                 
  1791.                 TESetSelect(newStart, newEnd, ((TextDataPtr) pData)->hTE);
  1792.                 TEDelete(((TextDataPtr) pData)->hTE);
  1793.                 TEInsert(&gReplaceString[1], gReplaceString[0], ((TextDataPtr) pData)->hTE);
  1794.                 TESetSelect(newStart, newStart+gReplaceString[0], ((TextDataPtr) pData)->hTE);                
  1795.                 oldNumLines -= (**(((TextDataPtr) pData)->hTE)).nLines;
  1796.                 
  1797.                 AdjustTE(pData, false);
  1798.                 AdjustScrollBars(pWindow, (oldNumLines > 0), (oldNumLines > 0), nil);
  1799.                 pData->changed = true;
  1800.                 }
  1801.             else
  1802.                 SysBeep(1);
  1803.  
  1804.             anErr = eActionAlreadyHandled;
  1805.             }
  1806.             break;
  1807.             
  1808.         case cFind:
  1809.         case cFindSelection:
  1810.             if (commandID == cFind)
  1811.                 {
  1812.                 if (ConductFindOrReplaceDialog(kFindWindowID) == cancel)    
  1813.                     break;
  1814.                 }
  1815.             else
  1816.                 {
  1817.                 gFindString[0] = (**((TextDataPtr) pData)->hTE).selEnd - (**((TextDataPtr) pData)->hTE).selStart;
  1818.                 BlockMoveData( (*(**((TextDataPtr) pData)->hTE).hText) + (**((TextDataPtr) pData)->hTE).selStart, &gFindString[1], gFindString[0]);
  1819.                 }
  1820.             
  1821.         // fall through from find or find selection
  1822.         case cFindAgain:
  1823.             {
  1824.             long     newStart, newEnd;
  1825.             Boolean    isBackwards = ((gEvent.modifiers & shiftKey) != 0);
  1826.             
  1827.             if (PerformSearch((**((TextDataPtr) pData)->hTE).hText,
  1828.                                 isBackwards ? (**((TextDataPtr) pData)->hTE).selStart : (**((TextDataPtr) pData)->hTE).selEnd,
  1829.                                 gFindString, gCaseSensitive, isBackwards, gWrapAround,
  1830.                                 &newStart, &newEnd))
  1831.                 {
  1832.                 TESetSelect(newStart, newEnd, ((TextDataPtr) pData)->hTE);
  1833.                 AdjustTE(pData, false);
  1834.                 AdjustScrollBars(pWindow, false, false, nil);
  1835.                 }
  1836.             else
  1837.                 SysBeep(1);
  1838.  
  1839.             anErr = eActionAlreadyHandled;
  1840.             }
  1841.             break;
  1842.             
  1843.         case cSelectAll:
  1844.             TESetSelect(0, (**((TextDataPtr) pData)->hTE).teLength, 
  1845.                         ((TextDataPtr) pData)->hTE);
  1846.             AdjustTE(pData, false);
  1847.             AdjustScrollBars(pWindow, false, false, nil);
  1848.             anErr = eActionAlreadyHandled;
  1849.             break;
  1850.         
  1851.         // save turns into save as if this is a new document or if the original wasn't
  1852.         // available for writing
  1853.         case cSave:
  1854.             if     (
  1855.                 (!pData->isWritable) || 
  1856.                 ( (pData->dataRefNum == -1) && (pData->resRefNum == -1) )
  1857.                 )
  1858.                 anErr = TextSaveAs(pWindow, pData);
  1859.             else
  1860.                 anErr = TextSave(pData);
  1861.             break;
  1862.             
  1863.         case cSaveAs:
  1864.             anErr = TextSaveAs(pWindow, pData);
  1865.             break;
  1866.             
  1867.         // SUPPORTED FONTS
  1868.         case cSelectFontStyle:            
  1869.         case cSelectFont:
  1870.             SaveCurrentUndoState(pData, cSelectFont);
  1871.             {
  1872.             Str255        itemName;
  1873.             Str255        menuName;
  1874.             TextStyle    theStyle;
  1875.             short        oldNumLines = (**(((TextDataPtr) pData)->hTE)).nLines;
  1876.             gxFont        fontID;
  1877.             
  1878.             GetMenuItemText( GetMenuHandle( menuResult>>16 ), menuResult & 0xFFFF, itemName );
  1879.             if (commandID == cSelectFont)
  1880.                 {
  1881.                 if (gMachineInfo.haveGX)    
  1882.                     {
  1883.                     GXFindFonts(nil, gxFamilyFontName, gxMacintoshPlatform, gxRomanScript, gxEnglishLanguage, itemName[0], &itemName[1], 1,1, &fontID);
  1884.                     theStyle.tsFont = FontToQD(fontID, nil);
  1885.                     }
  1886.                 else
  1887.                     GetFNum( itemName, &theStyle.tsFont );
  1888.                     
  1889.                 TESetStyle( doFont, &theStyle, true, ((TextDataPtr) pData)->hTE );
  1890.                 }
  1891.             else
  1892.                 {
  1893.                 BlockMoveData((**GetMenuHandle( menuResult>>16 )).menuData, menuName, sizeof(Str255));
  1894.                 
  1895.                 // find the font based upon family and style names
  1896.                 GXFindFonts(nil, gxFamilyFontName, gxMacintoshPlatform, gxRomanScript, gxEnglishLanguage, menuName[0], &menuName[1], 1,1, &fontID);
  1897.                 GXFindFonts(fontID, gxStyleFontName, gxMacintoshPlatform, gxRomanScript, gxEnglishLanguage, itemName[0], &itemName[1], 1,1, &fontID);
  1898.                 
  1899.                 // convert the font into a QD font ID and style bits (as appropriate)
  1900.                 theStyle.tsFont = FontToQD(fontID, &theStyle.tsFace);
  1901.                 TESetStyle( doFont + doFace, &theStyle, true, ((TextDataPtr) pData)->hTE );
  1902.                 }
  1903.                 
  1904.             TECalText(((TextDataPtr) pData)->hTE);
  1905.             oldNumLines -= (**(((TextDataPtr) pData)->hTE)).nLines;
  1906.             AdjustTE(pData, false);
  1907.             AdjustScrollBars(pWindow, (oldNumLines > 0), (oldNumLines > 0), nil);
  1908.             pData->changed = true;
  1909.             }
  1910.             break;
  1911.             
  1912.             
  1913.         // SUPPORTED STYLES
  1914.         case cPlain:
  1915.             ApplyFace(normal, pWindow, pData, cPlain);
  1916.             break;
  1917.             
  1918.         case cBold:
  1919.             ApplyFace(bold, pWindow, pData, cBold);
  1920.             break;
  1921.  
  1922.         case cItalic:
  1923.             ApplyFace(italic, pWindow, pData, cItalic);
  1924.             break;
  1925.             
  1926.         case cUnderline:
  1927.             ApplyFace(underline, pWindow, pData, cUnderline);
  1928.             break;
  1929.             
  1930.         case cOutline:
  1931.             ApplyFace(outline, pWindow, pData, cOutline);
  1932.             break;
  1933.             
  1934.         case cShadow:
  1935.             ApplyFace(shadow, pWindow, pData, cShadow);
  1936.             break;
  1937.  
  1938.         case cCondensed:
  1939.             ApplyFace(condense, pWindow, pData, cCondensed);
  1940.             break;
  1941.             
  1942.         case cExtended:
  1943.             ApplyFace(extend, pWindow, pData, cExtended);
  1944.             break;
  1945.             
  1946.     
  1947.         // SUPPORTED SIZES
  1948.         case cSize9:
  1949.             anErr = ApplySize(9, pWindow, pData, cSize9);
  1950.             break;
  1951.             
  1952.         case cSize10:
  1953.             anErr = ApplySize(10, pWindow, pData, cSize10);
  1954.             break;
  1955.             
  1956.         case cSize12:
  1957.             anErr = ApplySize(12, pWindow, pData, cSize12);
  1958.             break;
  1959.             
  1960.         case cSize14:
  1961.             anErr = ApplySize(14, pWindow, pData, cSize14);
  1962.             break;
  1963.             
  1964.         case cSize18:
  1965.             anErr = ApplySize(18, pWindow, pData, cSize18);
  1966.             break;
  1967.             
  1968.         case cSize24:
  1969.             anErr = ApplySize(24, pWindow, pData, cSize24);
  1970.             break;
  1971.             
  1972.         case cSize36:
  1973.             anErr = ApplySize(36, pWindow, pData, cSize36);
  1974.             break;
  1975.             
  1976.             
  1977.         // SUPPORTED SOUND COMMANDS
  1978.         case cRecord:
  1979.             {
  1980.             Handle    tempHandle;
  1981.  
  1982.             // allocate our prefered buffer if we can, but if we can't
  1983.             // make sure that at least a minimum amount of RAM is around
  1984.             // before recording.
  1985.             tempHandle = NewHandle(kPrefBufferSize);
  1986.             anErr = MemError();
  1987.             if (anErr != noErr)
  1988.                 {
  1989.                 tempHandle = NewHandle(kMinBufferSize);
  1990.                 anErr = MemError();
  1991.                 DisposeHandle(tempHandle);
  1992.                 tempHandle = nil;
  1993.                 }
  1994.             
  1995.             // if the preflight goes okay, do the record
  1996.             if (anErr == noErr)
  1997.                 {
  1998.                 Point    where = {50, 100};
  1999.                 
  2000.                 anErr = SndRecord(nil, where, siGoodQuality, (SndListHandle*) &tempHandle);
  2001.                 if (anErr == noErr)
  2002.                     {
  2003.                     DisposeHandle(((TextDataPtr) pData)->soundHandle);
  2004.                     ((TextDataPtr) pData)->soundHandle = tempHandle;
  2005.                     pData->changed = true;
  2006.                     }
  2007.                 else
  2008.                     DisposeHandle(tempHandle);
  2009.                     
  2010.                 if (anErr == userCanceledErr)
  2011.                     anErr = noErr;
  2012.                 }
  2013.             }
  2014.             break;
  2015.             
  2016.         case cPlay:
  2017.             if (((TextDataPtr) pData)->soundHandle)
  2018.                 (void) SndPlay(nil, (SndListHandle) ((TextDataPtr) pData)->soundHandle, false);
  2019.             break;
  2020.  
  2021.         case cErase:
  2022.             DisposeHandle(((TextDataPtr) pData)->soundHandle);
  2023.             ((TextDataPtr) pData)->soundHandle = nil;
  2024.             pData->changed = true;
  2025.             break;
  2026.             
  2027.         case cSpeak:
  2028.             DisposeOfSpeech(false);
  2029.             if (gSpeechChannel == nil)
  2030.                 anErr = NewSpeechChannel( &gCurrentVoice, &gSpeechChannel );
  2031.                 
  2032.             if ( anErr == noErr )
  2033.                 {
  2034.                 short    textLength, textStart;
  2035.                                     
  2036.                 // determine which text to speak
  2037.                 if ( (**((TextDataPtr) pData)->hTE).selEnd > (**((TextDataPtr) pData)->hTE).selStart )    // If there is a selection.
  2038.                     {
  2039.                     textLength = (**((TextDataPtr) pData)->hTE).selEnd - (**((TextDataPtr) pData)->hTE).selStart;
  2040.                     textStart = (**((TextDataPtr) pData)->hTE).selStart;
  2041.                     }
  2042.                 else                                            // No text selected.
  2043.                     {
  2044.                     textLength = (**((TextDataPtr) pData)->hTE).teLength;
  2045.                     textStart = 0;
  2046.                     }
  2047.                     
  2048.                     
  2049.                 gSpeakPtr = NewPtr(textLength);
  2050.                 anErr = MemError();
  2051.                 if (anErr == noErr)
  2052.                     {
  2053.                     BlockMoveData( *((**((TextDataPtr) pData)->hTE).hText) + textStart, gSpeakPtr, (Size) textLength );
  2054.                     anErr = SpeakText( gSpeechChannel, gSpeakPtr, textLength );
  2055.                     }
  2056.                 }
  2057.             break;
  2058.  
  2059.         case cStopSpeaking:
  2060.             DisposeOfSpeech(true);
  2061.             break;
  2062.             
  2063.         case cSelectVoiceSubMenu:
  2064.                 {
  2065.                 VoiceSpec    newSpec;
  2066.                 short        i, menuIndex;
  2067.                 Str255        itemText;
  2068.                 short        theVoiceCount;
  2069.                 MenuHandle     menu = GetMenuHandle(mVoices);
  2070.                 
  2071.                 // in order to change voices, we need to ditch the speaking
  2072.                 DisposeOfSpeech(true);
  2073.  
  2074.                 // get the name of the selected voice                
  2075.                 menuIndex = menuResult & 0xFFFF;
  2076.                 GetMenuItemText(menu, menuIndex, itemText);
  2077.                 
  2078.                 if (CountVoices( &theVoiceCount ) == noErr)
  2079.                     {
  2080.                     VoiceDescription    description;        // Info about a voice.
  2081.         
  2082.                     for (i = 1; i <= theVoiceCount; ++i)
  2083.                         {
  2084.                         if ( (GetIndVoice( i, &newSpec ) == noErr)  &&
  2085.                              (GetVoiceDescription( &newSpec, &description, sizeof(description) ) == noErr ) )
  2086.                             {
  2087.                             if (IUCompString( itemText, description.name ) == 0)
  2088.                                 break;
  2089.                             }
  2090.                         }
  2091.                     }
  2092.                     
  2093.                 gCurrentVoice = newSpec;
  2094.                 for (i = CountMItems(menu); i >= 1; --i)
  2095.                     CheckItem(menu, i, (menuIndex == i));
  2096.                 }
  2097.             break;
  2098.         
  2099.         
  2100.         case cSelectContents:
  2101.                 {
  2102.                 Str255    searchStr;
  2103.                 short    menuIndex;
  2104.                 long     newStart, newEnd;
  2105.                 
  2106.                 menuIndex = menuResult & 0xFFFF;
  2107.  
  2108.                 // get the search string for this menu item
  2109.                 anErr = TextGetContentsListItem(pData, menuIndex, nil, searchStr, nil);
  2110.                 
  2111.                 if (anErr == noErr)
  2112.                     {
  2113.                     if (PerformSearch(
  2114.                         (**((TextDataPtr) pData)->hTE).hText,
  2115.                         0,            // start at beginning of text
  2116.                         searchStr,
  2117.                         false,        // not case sensitive
  2118.                         false,        // forwards
  2119.                         false,        // wrap
  2120.                         &newStart, 
  2121.                         &newEnd))
  2122.                         {
  2123.                         
  2124.                         // <7>
  2125.                         
  2126.                         short     amount;
  2127.                         Point    newSelectionPt;
  2128.                         
  2129.                         // get QuickDraw offset of found text,  
  2130.                         // scroll that amount plus a line height,
  2131.                         // and add a fifth of the window for aesthetics (and  
  2132.                         // for slop to avoid fraction-of-line problems)
  2133.  
  2134.                         newSelectionPt = TEGetPoint(newEnd, ((TextDataPtr) pData)->hTE);
  2135.                         
  2136.                         amount = - newSelectionPt.v + pData->vScrollAmount;
  2137.                         amount += (pData->contentRect.bottom - pData->contentRect.top) / 5;
  2138.                         
  2139.                         SetControlAndClipAmount(pData->vScroll, &amount);
  2140.                         if (amount != 0)
  2141.                             {
  2142.                             DoScrollContent(pWindow, pData, 0, amount);
  2143.                             }
  2144.  
  2145.                         // move selection to beginning of found text 
  2146.                         // (are the Adjust calls necessary?)
  2147.                         
  2148.                         TESetSelect(newStart, newStart, ((TextDataPtr) pData)->hTE);    
  2149.                         AdjustTE(pData, false);
  2150.                         AdjustScrollBars(pWindow, false, false, nil);
  2151.  
  2152.                         }
  2153.                     else
  2154.                         {
  2155.                             // search failed
  2156.                             SysBeep(10);
  2157.                         }
  2158.                     }
  2159.                 }
  2160.             break;
  2161.         }
  2162.         
  2163.     return anErr;
  2164.     
  2165. } // TextCommand
  2166.  
  2167. // --------------------------------------------------------------------------------------------------------------
  2168.  
  2169. static OSErr    TextAdjustMenus(WindowRef pWindow, WindowDataPtr pData)
  2170. {
  2171. #pragma unused (pWindow)
  2172.  
  2173.     // enable the commands that we support for editable text document
  2174.     if (pData->originalFileType == 'TEXT')
  2175.         {        
  2176.         if (((TextDataPtr) pData)->prevCommandID != cNull)
  2177.             EnableCommand(cUndo);
  2178.             
  2179.         if ( (**((TextDataPtr) pData)->hTE).selEnd > (**((TextDataPtr) pData)->hTE).selStart )    // If there is a selection.
  2180.             {
  2181.             EnableCommand(cCut);
  2182.             EnableCommand(cCopy);
  2183.             EnableCommand(cClear);
  2184.             
  2185.             EnableCommand(cFindSelection);
  2186.             }
  2187.         
  2188.         TEFromScrap();
  2189.         if (TEGetScrapLength() > 0)
  2190.             EnableCommand(cPaste);
  2191.             
  2192.         EnableCommand(cSaveAs);
  2193.         EnableCommand(cSelectAll);
  2194.         
  2195.         EnableCommand(cFind);
  2196.         EnableCommand(cReplace);
  2197.         if (gFindString[0] != 0)
  2198.             {
  2199.             EnableCommand(cFindAgain);
  2200.             EnableCommand(cReplaceAgain);
  2201.             }
  2202.             
  2203.         // enable all fonts, select the font current, if that's what's best
  2204.         EnableCommand(cSelectFont);
  2205.         {
  2206.         short        mode = doFont;
  2207.         Str255        fontName, itemName;
  2208.         Str255        styleName;
  2209.         TextStyle    theStyle;
  2210.         Boolean        isCont;
  2211.         gxFont        fontID;
  2212.         
  2213.         isCont = TEContinuousStyle(&mode, &theStyle, ((TextDataPtr) pData)->hTE);
  2214.         if (isCont)
  2215.             {
  2216.             if (gMachineInfo.haveGX)
  2217.                 {
  2218.                 fontID = QDToFont(theStyle.tsFont, theStyle.tsFace);
  2219.  
  2220.                 fontName[0] = GXFindFontName(fontID, gxFamilyFontName, gxMacintoshPlatform, gxRomanScript, gxEnglishLanguage, &fontName[1], nil);
  2221.                 styleName[0] = GXFindFontName(fontID, gxStyleFontName, gxMacintoshPlatform, gxRomanScript, gxEnglishLanguage, &styleName[1], nil);
  2222.                 }
  2223.             else
  2224.                 {
  2225.                 GetFontName(theStyle.tsFont, fontName);
  2226.                 }
  2227.             }
  2228.  
  2229.  
  2230.             {
  2231.             MenuHandle    menu = GetMenuHandle( mFont );
  2232.             short        count = CountMItems(menu);
  2233.             short        index;
  2234.             
  2235.             for (index = 1; index <= count; ++index)
  2236.                 {
  2237.                 short    mark;
  2238.                 
  2239.                 GetItemMark(menu, index, &mark);
  2240.                 if (isCont)
  2241.                     {
  2242.                     GetMenuItemText( menu, index, itemName );
  2243.                     
  2244.                     // don't change the checkmark if it's a heirarchichal menu, because
  2245.                     // the mark actually holds the ID of sub-menu
  2246.                     if ((mark == noMark) || (mark == checkMark))
  2247.                         {
  2248.                         CheckItem(menu, index, EqualString(itemName, fontName, true, true) );
  2249.                         }
  2250.                     else
  2251.                         {
  2252.                         // if it is a sub menu, we check there too
  2253.                         MenuHandle    subMenu = GetMenuHandle(mark);
  2254.                         short        subCount = CountMItems(subMenu);
  2255.                         short        subIndex;
  2256.                         
  2257.                         if (EqualString(itemName, fontName, true, true))
  2258.                             {
  2259.                             SetItemStyle(menu, index, underline);
  2260.                             for (subIndex = 1; subIndex <= subCount; ++subIndex)
  2261.                                 {
  2262.                                 GetMenuItemText(subMenu, subIndex, itemName);
  2263.                                 CheckItem(subMenu, subIndex, EqualString(itemName, styleName, true, true) );
  2264.                                 }
  2265.                             }
  2266.                         else
  2267.                             {
  2268.                             SetItemStyle(menu, index, normal);
  2269.                             for (subIndex = 1; subIndex <= subCount; ++subIndex)
  2270.                                 CheckItem(subMenu, subIndex, false );
  2271.                             }
  2272.                         }
  2273.                     }
  2274.                 else
  2275.                     {
  2276.                     if ((mark == noMark) || (mark == checkMark))
  2277.                         CheckItem(menu, index, false);
  2278.                     else
  2279.                         SetItemStyle(menu, index, normal);
  2280.                     }
  2281.                 }
  2282.             }
  2283.         }
  2284.  
  2285.         // enable the sizes, and outline what's currently valid
  2286.         {
  2287.         short        mode;
  2288.         TextStyle    theStyle;
  2289.         Boolean        isCont;
  2290.         short        whichToCheck;
  2291.         
  2292.         // find out the continuous run of sizes
  2293.         whichToCheck = 0;
  2294.         mode = doSize;
  2295.         if (TEContinuousStyle(&mode, &theStyle, ((TextDataPtr) pData)->hTE))
  2296.             {            
  2297.             whichToCheck = theStyle.tsSize;
  2298.             
  2299.             // default font size -> proper size
  2300.             if (whichToCheck == 0)
  2301.                 whichToCheck = GetDefFontSize();
  2302.             }
  2303.             
  2304.         // find out the font runs
  2305.         mode = doFont;
  2306.         isCont = TEContinuousStyle(&mode, &theStyle, ((TextDataPtr) pData)->hTE);
  2307.         
  2308.         EnableCommandCheckStyle(cSize9,  whichToCheck == 9, (isCont & RealFont(theStyle.tsFont, 9)) ? outline : normal);
  2309.         EnableCommandCheckStyle(cSize10, whichToCheck == 10, (isCont & RealFont(theStyle.tsFont, 10)) ? outline : normal);
  2310.         EnableCommandCheckStyle(cSize12, whichToCheck == 12, (isCont & RealFont(theStyle.tsFont, 12)) ? outline : normal);
  2311.         EnableCommandCheckStyle(cSize14, whichToCheck == 14, (isCont & RealFont(theStyle.tsFont, 14)) ? outline : normal);
  2312.         EnableCommandCheckStyle(cSize18, whichToCheck == 18, (isCont & RealFont(theStyle.tsFont, 18)) ? outline : normal);
  2313.         EnableCommandCheckStyle(cSize24, whichToCheck == 24, (isCont & RealFont(theStyle.tsFont, 24)) ? outline : normal);
  2314.         EnableCommandCheckStyle(cSize36, whichToCheck == 36, (isCont & RealFont(theStyle.tsFont, 36)) ? outline : normal);            
  2315.         }
  2316.         
  2317.         {
  2318.         short        mode = doFace;
  2319.         TextStyle    theStyle;
  2320.         Style        legalStyles;
  2321.         
  2322.         if (!TEContinuousStyle(&mode, &theStyle, ((TextDataPtr) pData)->hTE))
  2323.             {
  2324.             theStyle.tsFace = normal;
  2325.             EnableCommandCheck(cPlain, false);
  2326.             }
  2327.         else
  2328.             EnableCommandCheck(cPlain, theStyle.tsFace == normal);
  2329.             
  2330.         // <39> use the script manager to determine legal styles for this
  2331.         // run of text.  If the legal styles are zero (trap unimplemented),
  2332.         // then we assume all styles.
  2333.         legalStyles = GetScriptVariable(GetScriptManagerVariable(smKeyScript), smScriptValidStyles);
  2334.         if (legalStyles == 0)
  2335.             legalStyles = 0xFFFF;
  2336.             
  2337.         if (legalStyles & bold)
  2338.             EnableCommandCheck(cBold,             theStyle.tsFace & bold);
  2339.         if (legalStyles & italic)
  2340.             EnableCommandCheck(cItalic,         theStyle.tsFace & italic);
  2341.         if (legalStyles & underline)
  2342.             EnableCommandCheck(cUnderline,         theStyle.tsFace & underline);
  2343.         if (legalStyles & outline)
  2344.             EnableCommandCheck(cOutline,         theStyle.tsFace & outline);
  2345.         if (legalStyles & shadow)
  2346.             EnableCommandCheck(cShadow,         theStyle.tsFace & shadow);
  2347.         if (legalStyles & condense)
  2348.             EnableCommandCheck(cCondensed,         theStyle.tsFace & condense);
  2349.         if (legalStyles & extend)
  2350.             EnableCommandCheck(cExtended,         theStyle.tsFace & extend);
  2351.         }
  2352.         
  2353.         }
  2354.  
  2355.     // enable commands related to speaking the content if we have support for that
  2356.     if (gMachineInfo.haveTTS)
  2357.         {
  2358.         // if we are speaking, we can stop
  2359.         if (gSpeechChannel) 
  2360.             EnableCommand(cStopSpeaking);
  2361.  
  2362.         // even while speaking, you can re-speak or select a new voice
  2363.         EnableCommand(cSpeak);
  2364.         EnableCommand(cSelectVoice);
  2365.         EnableCommand(cSelectVoiceSubMenu);
  2366.         
  2367.         if ( (**((TextDataPtr) pData)->hTE).selEnd > (**((TextDataPtr) pData)->hTE).selStart )    // If there is a selection.
  2368.             ChangeCommandName(cSpeak, kTextStrings, iSpeakSelection);
  2369.         else
  2370.             ChangeCommandName(cSpeak, kTextStrings, iSpeakAll);
  2371.             
  2372.         }
  2373.         
  2374.     // enable the correct controls to go with sound input/output
  2375.     if (((TextDataPtr) pData)->soundHandle)
  2376.         EnableCommand(cPlay);
  2377.     if (pData->originalFileType == 'TEXT')
  2378.         {
  2379.         if (((TextDataPtr) pData)->soundHandle)
  2380.             EnableCommand(cErase);
  2381.         else
  2382.             {
  2383.             if (gMachineInfo.haveRecording)
  2384.                 EnableCommand(cRecord);
  2385.             }
  2386.         }
  2387.     
  2388.     // enable the contents menu, if any         
  2389.     (void) TextAdjustContentsMenu(pData);
  2390.     
  2391.     // enable commands that we support at all times
  2392.     if (GetControlMaximum(pData->vScroll) != 0)
  2393.         {
  2394.         EnableCommand(cNextPage);
  2395.         EnableCommand(cPreviousPage);
  2396.         }
  2397.     
  2398.     return noErr;
  2399.     
  2400. } // TextAdjustMenus
  2401.  
  2402. // --------------------------------------------------------------------------------------------------------------
  2403.  
  2404. static OSErr    TextGetDocumentRect(WindowRef pWindow, WindowDataPtr pData, 
  2405.             LongRect * documentRectangle, Boolean forGrow)
  2406. {
  2407. #pragma unused (pWindow)
  2408.     
  2409.     Rect    theRect = pData->contentRect;
  2410.     Rect    maxRect = (**GetGrayRgn()).rgnBBox;
  2411.     
  2412.     if ( (!forGrow) && (!(((TextDataPtr) pData)->insideClickLoop) ) )
  2413.         RecalcTE(pData, false);
  2414.     
  2415.     theRect.bottom = CalculateTextEditHeight(((TextDataPtr) pData)->hTE);
  2416.     theRect.bottom += kMargins*2;
  2417.     theRect.right = maxRect.right;
  2418.         
  2419.     if (theRect.bottom < pData->contentRect.bottom)
  2420.         theRect.bottom = pData->contentRect.bottom;
  2421.         
  2422.     if (forGrow)
  2423.         theRect.bottom = maxRect.bottom-kScrollBarSize;
  2424.         
  2425.     RectToLongRect(&theRect, documentRectangle);
  2426.     
  2427.     return noErr;
  2428.     
  2429. } // TextGetDocumentRect
  2430.  
  2431. // --------------------------------------------------------------------------------------------------------------
  2432.  
  2433. static OSErr    TextGetBalloon(WindowRef pWindow, WindowDataPtr pData, 
  2434.         Point *localMouse, short * returnedBalloonIndex, Rect *returnedRectangle)
  2435. {
  2436. #pragma unused (pWindow, pData, localMouse, returnedRectangle)
  2437.  
  2438.     *returnedBalloonIndex = iHelpTextContent;
  2439.     
  2440.     return noErr;
  2441.     
  2442. } // TextGetBalloon
  2443.  
  2444. // --------------------------------------------------------------------------------------------------------------
  2445.  
  2446. static long TextCalculateIdleTime(WindowRef pWindow, WindowDataPtr pData)
  2447. {
  2448. #pragma unused (pWindow, pData)
  2449.  
  2450.     if ( (gMachineInfo.amInBackground) || (! (**(((TextDataPtr) pData)->hTE)).active) )
  2451.         return(0x7FFFFFF);
  2452.     else
  2453.         return(1);
  2454.         
  2455. } // TextCalculateIdleTime
  2456.  
  2457. // --------------------------------------------------------------------------------------------------------------
  2458.  
  2459. static OSErr    TextAdjustCursor(WindowRef pWindow, WindowDataPtr pData, 
  2460.                         Point * localMouse, Rect * globalRect)
  2461. {
  2462. #pragma unused (pWindow, globalRect)
  2463.  
  2464.     OSErr            anErr = noErr;
  2465.     CursHandle        theCross;
  2466.     RgnHandle        hilightRgn;
  2467.     
  2468.     if (gMachineInfo.haveDragMgr)
  2469.         {
  2470.         hilightRgn = NewRgn();
  2471.         TEGetHiliteRgn(hilightRgn, ((TextDataPtr) pData)->hTE);
  2472.         if (PtInRgn(*localMouse, hilightRgn))
  2473.             {
  2474.             SetCursor(&qd.arrow);
  2475.             DisposeRgn(hilightRgn);
  2476.             return eActionAlreadyHandled;    
  2477.             }
  2478.     
  2479.         DisposeRgn(hilightRgn);
  2480.         }
  2481.         
  2482.     theCross = GetCursor(iBeamCursor);
  2483.     if (theCross)
  2484.         {
  2485.         char    oldState;
  2486.         
  2487.         oldState = HGetState((Handle) theCross);
  2488.         HLock((Handle) theCross);
  2489.         SetCursor(*theCross);
  2490.         HSetState((Handle) theCross, oldState);
  2491.         anErr = eActionAlreadyHandled;
  2492.         }
  2493.         
  2494.     return anErr;
  2495.     
  2496. } // TextAdjustCursor
  2497.  
  2498. // --------------------------------------------------------------------------------------------------------------
  2499.  
  2500. short gNilCaretProc[] = { 
  2501.                         0x584F,         // ADDQ.W    #$4, A7
  2502.                         0x4E75};        // RTS
  2503.  
  2504. // --------------------------------------------------------------------------------------------------------------
  2505.  
  2506. static OSErr    TextPrintPage(WindowRef pWindow, WindowDataPtr pData,
  2507.                     Rect * pageRect, long *pageNum)
  2508. {
  2509. #pragma unused (pWindow)
  2510.  
  2511.     OSErr        anErr = noErr;
  2512.     short        footerHeight;
  2513.     TEHandle    hTE;
  2514.     Rect        areaForThisPage;
  2515.     short        ourPage = 1;
  2516.     Boolean        documentHasFormControl = Count1Resources(kFormResource) != 0;
  2517.     
  2518.     // calculate area for the footer (page number)
  2519.     {
  2520.     FontInfo    theInfo;
  2521.     
  2522.     TextFont(0);
  2523.     TextSize(0);
  2524.     TextFace(normal);
  2525.     GetFontInfo(&theInfo);
  2526.     footerHeight = (theInfo.ascent + theInfo.descent + theInfo.leading) << 1;
  2527.     }
  2528.     
  2529.     // duplicate the text edit record, disable the selection before swapping the new port in
  2530.     hTE = ((TextDataPtr) pData)->hTE;
  2531.     TEDeactivate(hTE);
  2532.     
  2533.     anErr = HandToHand((Handle*) &hTE);
  2534.     nrequire(anErr, DuplicateTE);
  2535.  
  2536.     // turn off outline hilighting -- because the window is disabled while
  2537.     // printing is going on, but we don't want that disabled hilight to draw
  2538.     TEFeatureFlag(teFOutlineHilite, teBitClear, ((TextDataPtr) pData)->hTE);
  2539.         
  2540.     // now HERE'S a real hack!  Under certain conditions, Text Edit will draw the
  2541.     // cursor, even if you said the edit record is inactive!  This happens when
  2542.     // the internal state sez that the cursor hasn't been drawn yet.  Lucky
  2543.     // for us, the caret is drawn through a hook, which we replace with a NOP.
  2544.     (**hTE).caretHook = (CaretHookUPP) gNilCaretProc;
  2545.     
  2546.     // point the rectangles to be the page rect minus the footer
  2547.     areaForThisPage = *pageRect;
  2548.     areaForThisPage.bottom -= footerHeight;
  2549.     if (gMachineInfo.haveGX)
  2550.         InsetRect(&areaForThisPage, kGXPrintMargins, kGXPrintMargins);
  2551.     else
  2552.         InsetRect(&areaForThisPage, kPrintMargins, kPrintMargins);
  2553.     (**hTE).viewRect = (**hTE).destRect = areaForThisPage;
  2554.  
  2555.     // recalculate the line breaks
  2556.     TECalText(hTE);
  2557.  
  2558.     // point it at the printing port.
  2559.     (**hTE).inPort = qd.thePort;
  2560.  
  2561.     // now loop over all pages doing page breaking until we find our current
  2562.     // page, which we print, and then return.
  2563.     {
  2564.     Rect    oldPageHeight = (**hTE).viewRect;
  2565.     short    currentLine = 0;
  2566.     long    prevPageHeight = 0;
  2567.  
  2568.     while (ourPage <= *pageNum)
  2569.         {
  2570.         long    currentPageHeight = 0;
  2571.                     
  2572.         // calculate the height including the current page, breaks
  2573.         // when one of three things happen:
  2574.         // 1) adding another line to this page would go beyond the length of the page
  2575.         // 2) a picture needs to be broken to the next page (NOT YET IMPLEMENTED)
  2576.         // 3) we run out of lines for the document 
  2577.         // 4) if the line has a page break (defined as a non breaking space w/o a PICT)
  2578.  
  2579.         // POTENTIAL BUG CASES:
  2580.         // If a single line > the page height.  Can that happen?  If so, we need to 
  2581.         // add something to handle it.
  2582.         do
  2583.             {
  2584.             long currentLineHeight;
  2585.             
  2586.             // zero based count -- but one based calls to TEGetHeight 
  2587.             currentLineHeight = TEGetHeight(currentLine+1, currentLine+1, hTE);
  2588.                 
  2589.             // if adding this line would just be too much, break out of here
  2590.             if ((currentLineHeight + currentPageHeight) > (areaForThisPage.bottom - areaForThisPage.top))
  2591.                 break;
  2592.                 
  2593.             ++ currentLine;
  2594.             currentPageHeight += currentLineHeight;
  2595.             
  2596.             // if this line had a page break on it, break out of pagination
  2597.             if (documentHasFormControl && LineHasPageBreak(currentLine-1, hTE))
  2598.                 break;
  2599.                 
  2600.             } while (currentLine < (**hTE).nLines);
  2601.         
  2602.         // if this the page we are trying to print
  2603.         if (ourPage == *pageNum)
  2604.             {
  2605.             Str255        pageString;
  2606.             RgnHandle    oldRgn = NewRgn();
  2607.             
  2608.             // move onto the next page via offset by the previous pages -- but
  2609.             // clip to the current page height because we wouldn't want to see
  2610.             // half of a line from the next page at the bottom of a page.
  2611.             OffsetRect(&oldPageHeight, 0, -(prevPageHeight));
  2612.             oldPageHeight.bottom = oldPageHeight.top + currentPageHeight;
  2613.             (**hTE).destRect = oldPageHeight;
  2614.             
  2615.             // clip to this area as well
  2616.             areaForThisPage.bottom = areaForThisPage.top + currentPageHeight;
  2617.             GetClip(oldRgn);
  2618.             ClipRect(&areaForThisPage);
  2619.             
  2620.             // draw the edit record, plus our cool pictures    
  2621.             TEUpdate(&areaForThisPage, hTE); 
  2622.             DrawPictures(pData, hTE);
  2623.             
  2624.             // restore the clip
  2625.             SetClip(oldRgn);
  2626.             DisposeRgn(oldRgn);
  2627.             
  2628.             // Draw the page string at the bottom of the page, centered
  2629.             pageString[0] = 2;
  2630.             pageString[1] = '-';
  2631.             NumToString(*pageNum, &pageString[2]);
  2632.             pageString[0] += pageString[2];
  2633.             pageString[2] = ' ';
  2634.             pageString[++pageString[0]] = ' ';
  2635.             pageString[++pageString[0]] = '-';
  2636.             
  2637.             MoveTo(
  2638.                 pageRect->left + 
  2639.                     ((pageRect->right - pageRect->left) >> 1) - 
  2640.                     (StringWidth(pageString)>>1),
  2641.                 pageRect->bottom - kPrintMargins);
  2642.                 
  2643.             DrawString(pageString);
  2644.             
  2645.             // if we have completed all pages
  2646.             if (currentLine >= (**hTE).nLines)
  2647.                 {
  2648.                 // tell it to stop printing
  2649.                 *pageNum = -1;
  2650.                 }
  2651.                 
  2652.             // get out of here!
  2653.             break;
  2654.             }
  2655.     
  2656.         // move onto the next page via count
  2657.         ++ourPage;
  2658.         
  2659.         // and the list of pages before now includes this page we just finished
  2660.         prevPageHeight += currentPageHeight;
  2661.         
  2662.         }
  2663.     
  2664.     }
  2665.     
  2666.     
  2667.     // restore text for visible page if done
  2668.     if (*pageNum == -1)
  2669.         {
  2670.         TEFeatureFlag(teFOutlineHilite, teBitSet, ((TextDataPtr) pData)->hTE);
  2671.         TECalText(((TextDataPtr) pData)->hTE);
  2672.         if (pData->originalFileType != 'ttro')
  2673.             TEActivate(((TextDataPtr) pData)->hTE);
  2674.         }
  2675.         
  2676. // FALL THROUGH EXCEPTION HANDLING
  2677.  
  2678.     // Dispose this way to avoid disposing of any owned objects
  2679.     DisposeHandle((Handle) hTE);
  2680. DuplicateTE:
  2681.  
  2682.     return anErr;
  2683.     
  2684. } // TextPrintPage
  2685.  
  2686. // --------------------------------------------------------------------------------------------------------------
  2687.  
  2688. static OSErr    TextMakeWindow(WindowRef pWindow, WindowDataPtr pData)
  2689. {
  2690.     #pragma unused(pWindow)
  2691.  
  2692.     OSErr                anErr = noErr;
  2693.  
  2694.     pData->bumpUntitledCount    = true;
  2695.     
  2696.     pData->pScrollContent         = (ScrollContentProc)    TextScrollContent;
  2697.     pData->pAdjustSize             = (AdjustSizeProc)        TextAdjustSize;
  2698.     pData->pGetDocumentRect     = (GetDocumentRectProc)    TextGetDocumentRect;
  2699.     pData->pAdjustMenus             = (AdjustMenusProc)        TextAdjustMenus;
  2700.     pData->pCommand                 = (CommandProc)            TextCommand;
  2701.  
  2702.     pData->pCloseWindow         = (CloseWindowProc)        TextCloseWindow;
  2703.     pData->pFilterEvent         = (FilterEventProc)        TextFilterEvent;
  2704.     pData->pActivateEvent         = (ActivateEventProc)    TextActivateEvent;
  2705.     pData->pUpdateWindow         = (UpdateWindowProc)    TextUpdateWindow;
  2706.     pData->pPrintPage             = (PrintPageProc)        TextPrintPage;
  2707.  
  2708.     // we only support keydowns and editing for modifable docs
  2709.     if (pData->originalFileType != 'ttro')
  2710.         {
  2711.         pData->pKeyEvent             = (KeyEventProc)            TextKeyEvent;
  2712.         pData->pContentClick        = (ContentClickProc)        TextContentClick;
  2713.         pData->pAdjustCursor        = (AdjustCursorProc)        TextAdjustCursor;
  2714.         pData->pGetBalloon            = (GetBalloonProc)            TextGetBalloon;
  2715.         pData->pCalculateIdleTime    = (CalculateIdleTimeProc)    TextCalculateIdleTime;
  2716.         
  2717.         // We can always reference our Drag handlers, because they will not be called if we
  2718.         // don't have the Drag Manager available. We needn't check here (it would be redundant).
  2719.         pData->pDragTracking        = (DragTrackingProc)    TextDragTracking;
  2720.         pData->pDragReceive            = (DragReceiveProc)        TextDragReceive;
  2721.  
  2722.         pData->documentAcceptsText    = true;
  2723.         }
  2724.         
  2725.     // leave room for the grow area at bottom
  2726.     pData->hasGrow                = true;
  2727.     pData->contentRect.bottom    -= kScrollBarSize;    
  2728.     if ((pData->contentRect.right - pData->contentRect.left) > kOnePageWidth)
  2729.         pData->contentRect.right = pData->contentRect.left + kOnePageWidth;
  2730.         
  2731.     ((TextDataPtr) pData)->hTE    = TEStyleNew(&pData->contentRect, &pData->contentRect);
  2732.     anErr = MemError();
  2733.     nrequire(anErr, TENewFailed);
  2734.         
  2735.     pData->hScrollAmount        = 0;
  2736.     pData->vScrollAmount        = TEGetHeight(0, 0, ((TextDataPtr) pData)->hTE);
  2737.  
  2738.     TEAutoView(true, ((TextDataPtr) pData)->hTE);
  2739.  
  2740.     // Setup our click loop to handle autoscrolling
  2741.     ((TextDataPtr) pData)->docClick = (**(((TextDataPtr) pData)->hTE)).clickLoop;
  2742.     TESetClickLoop(gMyClickLoop, ((TextDataPtr) pData)->hTE);
  2743.     
  2744.     // if we have a data fork, read the contents into the record    
  2745.     if (pData->dataRefNum != -1)
  2746.         {
  2747.         long    dataSize;
  2748.         
  2749.         GetEOF(pData->dataRefNum, &dataSize);
  2750.         if (dataSize > kMaxLength)
  2751.             anErr = eDocumentTooLarge;
  2752.         else
  2753.             {
  2754.             Handle    tempHandle = NewHandle(dataSize);
  2755.             anErr = MemError();
  2756.             if (anErr == noErr)
  2757.                 {
  2758.                 // read the text in
  2759.                 SetFPos(pData->dataRefNum, fsFromStart, 0);
  2760.                 anErr = FSRead(pData->dataRefNum, &dataSize, * tempHandle);                
  2761.  
  2762.                 // then insert it.
  2763.                 if (anErr == noErr)
  2764.                     {
  2765.                     HLock(tempHandle);
  2766.                     TEStyleInsert(*tempHandle, dataSize, nil, ((TextDataPtr) pData)->hTE);
  2767.                     anErr = MemError();
  2768.                     }
  2769.                 DisposeHandle(tempHandle);
  2770.                 }
  2771.             
  2772.             }
  2773.         
  2774.         }
  2775.     nrequire(anErr, ReadData);
  2776.     
  2777.     // if we have a resource fork, read the contents
  2778.     if (pData->resRefNum != -1)
  2779.         {
  2780.         short    oldResFile = CurResFile();
  2781.         Handle    theStyle;
  2782.         
  2783.         // read the style information
  2784.         UseResFile(pData->resRefNum);
  2785.         theStyle = Get1Resource('styl', 128);
  2786.         if (theStyle)
  2787.             {
  2788.             HNoPurge(theStyle);
  2789.             TEUseStyleScrap(0, 32767, (StScrpHandle) theStyle, true, ((TextDataPtr) pData)->hTE);
  2790.             ReleaseResource(theStyle);
  2791.             }
  2792.  
  2793.         // if we have sound, load it in and detach it
  2794.         {
  2795.         Handle    soundHandle = Get1Resource('snd ', kSoundBase);
  2796.         if (soundHandle)
  2797.             {
  2798.             HNoPurge(soundHandle);
  2799.             DetachResource(soundHandle);
  2800.             ((TextDataPtr) pData)->soundHandle = soundHandle;
  2801.             }
  2802.         }
  2803.  
  2804.  
  2805.         UseResFile(oldResFile);
  2806.         }
  2807.             
  2808.     // hook out drawing of the non-breaking space for read only documents,
  2809.     // for modifiable documents, enable outline hiliting (ie, when TE window
  2810.     // isn't in front, show the gray outline)
  2811.     if (pData->originalFileType == 'ttro')
  2812.         {
  2813.         UniversalProcPtr    hookRoutine = (UniversalProcPtr)gMyDrawGlue;
  2814.         
  2815.         TECustomHook(intDrawHook, &hookRoutine, ((TextDataPtr) pData)->hTE);
  2816.         }
  2817.     else
  2818.         {
  2819.         TEFeatureFlag(teFOutlineHilite, teBitSet, ((TextDataPtr) pData)->hTE);
  2820.         }
  2821.  
  2822.     // make a TSM document if this is editable
  2823.     if     (
  2824.         (pData->originalFileType != 'ttro') &&
  2825.         (gMachineInfo.haveTSMTE)
  2826.         )
  2827.         {
  2828.         OSType    supportedInterfaces[1];
  2829.  
  2830.         supportedInterfaces[0] = kTSMTEInterfaceType;
  2831.         
  2832.         if (NewTSMDocument(1, supportedInterfaces, 
  2833.             &pData->docTSMDoc, (long)&pData->docTSMRecHandle) == noErr)
  2834.             {
  2835.             long response;
  2836.  
  2837.             (**(pData->docTSMRecHandle)).textH                 = ((TextDataPtr) pData)->hTE;
  2838.             if ((Gestalt(gestaltTSMTEVersion, &response) == noErr) && (response == gestaltTSMTE1))
  2839.                 (**(pData->docTSMRecHandle)).preUpdateProc     = gTSMPreUpdateProc;
  2840.             (**(pData->docTSMRecHandle)).postUpdateProc     = gTSMPostUpdateProc;
  2841.             (**(pData->docTSMRecHandle)).updateFlag         = kTSMTEAutoScroll;
  2842.             (**(pData->docTSMRecHandle)).refCon             = (long)pData;
  2843.             }
  2844.         }
  2845.  
  2846.     // now we have added text, so adjust views and such as needed
  2847.     TESetSelect(0, 0, ((TextDataPtr) pData)->hTE);
  2848.     RecalcTE(pData, true);
  2849.     AdjustTE(pData, true);
  2850.  
  2851.     // ???? Hack to get around a 7.0 TextEdit bug.  If you are pasting a multiple
  2852.     // line clipboard into TE, *and* the TextEdit record is new, *and* the selection
  2853.     // is at the begining of the doc (0,0 as above), *and* you haven't moved the
  2854.     // cursor around at all, then TE pastes thinking it's at the end of the line,
  2855.     // when it really should be at the begining.  Then if you <cr> with the cursor
  2856.     // visible, it'll leave a copy behind.  
  2857.     
  2858.     // I'm not happy with this, but I don't know another way around the problem.
  2859.     if (pData->originalFileType != 'ttro') 
  2860.         {
  2861.         TEKey(0x1F, ((TextDataPtr) pData)->hTE);
  2862.         TEKey(0x1E, ((TextDataPtr) pData)->hTE);
  2863.         }
  2864.     
  2865.     // <39> if this is a new document, convert the "system size", "system font", and
  2866.     // "application font" into real font IDs and sizes.  This is so that
  2867.     // if someone saves this document and opens it with another script
  2868.     // system, they don't get all huffy that the font changed on them.
  2869.     // It also solves problems with cut and paste to applications too stupid
  2870.     // to know that "zero" means system size.
  2871.     if (pData->dataRefNum == -1)
  2872.         {
  2873.         TEHandle    hTE = ((TextDataPtr) pData)->hTE;
  2874.         short        mode = doAll;
  2875.         TextStyle    theStyle;
  2876.     
  2877.         TEContinuousStyle(&mode, &theStyle, hTE);
  2878.         if (theStyle.tsSize == 0)
  2879.             theStyle.tsSize = GetDefFontSize();
  2880.         if (theStyle.tsFont == systemFont)
  2881.             theStyle.tsFont = GetSysFont();
  2882.         if (theStyle.tsFont == applFont)
  2883.             theStyle.tsFont = GetAppFont();
  2884.             
  2885.         mode = doAll;
  2886.         TESetStyle(mode, &theStyle, false, hTE);
  2887.         }
  2888.  
  2889.     // if stationary, use untitled and close down the files
  2890.     if (pData->originalFileType == 'sEXT')
  2891.         {
  2892.         pData->originalFileType = 'TEXT';
  2893.         pData->openAsNew = true;
  2894.         if (pData->resRefNum != -1)
  2895.             CloseResFile(pData->resRefNum);
  2896.         if (pData->dataRefNum != -1)
  2897.             FSClose(pData->dataRefNum);
  2898.         pData->resRefNum = pData->dataRefNum = -1;
  2899.         }
  2900.         
  2901.     // initalize undo information
  2902.     ((TextDataPtr) pData)->prevCommandID = cNull;
  2903.     
  2904.     // if we have voices, add them to the menu
  2905.     if ( (gMachineInfo.haveTTS) && (!gAddedVoices) )
  2906.         {
  2907.         short    theVoiceCount;
  2908.         short    i, item;
  2909.         
  2910.         if (CountVoices( &theVoiceCount ) == noErr)
  2911.             {
  2912.             VoiceSpec            spec;                // A voice to add to the menu.
  2913.             VoiceDescription    description;        // Info about a voice.
  2914.             MenuHandle            voicesMenu = GetMenuHandle(mVoices);
  2915.  
  2916.             anErr = GetVoiceDescription( nil, &description, sizeof(description) );
  2917.             if (anErr == noErr)
  2918.                 {
  2919.                 gCurrentVoice = description.voice;
  2920.                 for (i = 1; i <= theVoiceCount; ++i)
  2921.                     {
  2922.                     if ( (GetIndVoice( i, &spec ) == noErr)  &&
  2923.                          (GetVoiceDescription( &spec, &description, sizeof(description) ) == noErr ) )
  2924.                         {
  2925.                         short    menuCount = CountMItems( voicesMenu );
  2926.                         
  2927.                         // first one we are adding == get rid of item already there
  2928.                         if ( (i == 1)  && (menuCount > 0) )
  2929.                             {
  2930.                             DeleteMenuItem( voicesMenu, 1 );
  2931.                             --menuCount;
  2932.                             }
  2933.                             
  2934.                         for ( item = 1; item <= menuCount; ++item )
  2935.                             {
  2936.                             Str255    itemText;
  2937.                             
  2938.                             GetMenuItemText( voicesMenu, item, itemText );
  2939.                             /*1st > 2nd*/
  2940.                             if ( IUCompString( itemText, description.name ) == 1 )
  2941.                                 break;                        // Found where name goes in list.
  2942.                             }
  2943.         
  2944.                         InsertMenuItem( voicesMenu, "\p ", item - 1 );
  2945.                         SetMenuItemText( voicesMenu, item, description.name );
  2946.                         
  2947.                         CheckItem(voicesMenu, item, 
  2948.                             ((gCurrentVoice.creator == spec.creator) && (gCurrentVoice.id == spec.id)) );
  2949.                         }
  2950.                     }
  2951.     
  2952.                 }
  2953.             
  2954.             gAddedVoices = true;
  2955.             }
  2956.             
  2957.         } // end of adding voices
  2958.         
  2959.     return noErr;
  2960.     
  2961. // EXCEPTION HANDLING
  2962. ReadData:
  2963.     TEDispose(((TextDataPtr) pData)->hTE);
  2964.     
  2965. TENewFailed:
  2966.  
  2967.     return anErr;
  2968.     
  2969. } // TextMakeWindow
  2970.  
  2971.  
  2972. // --------------------------------------------------------------------------------------------------------------
  2973.  
  2974. OSErr    TextPreflightWindow(PreflightPtr pPreflightData)
  2975. {    
  2976.     pPreflightData->continueWithOpen     = true;
  2977.     pPreflightData->wantVScroll            = true;
  2978.     pPreflightData->doZoom                = true;
  2979.     pPreflightData->makeProcPtr         = TextMakeWindow;
  2980.     if (pPreflightData->fileType != 'ttro')
  2981.         pPreflightData->openKind            = fsRdWrPerm;
  2982.     
  2983.     pPreflightData->storageSize         = sizeof(TextDataRecord);
  2984.  
  2985.     // get strings that mark the picture
  2986.     GetIndString(gPictMarker1, kTextStrings, iPictureMarker1);
  2987.     GetIndString(gPictMarker2, kTextStrings, iPictureMarker2);
  2988.     
  2989.     return noErr;
  2990.     
  2991. } // TextPreflightWindow
  2992.  
  2993. // --------------------------------------------------------------------------------------------------------------
  2994.  
  2995. void TextGetFileTypes(OSType * pFileTypes, OSType * pDocumentTypes, short * numTypes)
  2996. {
  2997.     pFileTypes[*numTypes]         = 'TEXT';
  2998.     pDocumentTypes[*numTypes]     = kTextWindow;
  2999.     (*numTypes)++;
  3000.         
  3001.     pFileTypes[*numTypes]         = 'ttro';
  3002.     pDocumentTypes[*numTypes]     = kTextWindow;
  3003.     (*numTypes)++;
  3004.         
  3005.     pFileTypes[*numTypes]         = 'sEXT';
  3006.     pDocumentTypes[*numTypes]     = kTextWindow;
  3007.     (*numTypes)++;
  3008.         
  3009. } // TextGetFileTypes
  3010.  
  3011. // --------------------------------------------------------------------------------------------------------------
  3012.  
  3013.  
  3014.  
  3015. // TextAddContentsMenu checks if there is a contents list and, if there
  3016. // is, creates a new menu handle for the contents list and fills it with 
  3017. // the appropriate visible items
  3018.  
  3019. void TextAddContentsMenu(WindowDataPtr pData)
  3020. {
  3021.     MenuHandle    contentsMenu;
  3022.     Str255        menuStr;
  3023.     short        totalItems;
  3024.     short        index;
  3025.     OSErr        err;
  3026.     
  3027.     contentsMenu = GetMenuHandle(mContents);
  3028.     require(contentsMenu == nil, ContentsMenuAlreadyInstalled);
  3029.     
  3030.     // Is there a contents list?  If so, get the menu name 
  3031.     // and the number of items in the list
  3032.     
  3033.     if (TextGetContentsListItem(pData, 0, menuStr, nil, &totalItems) == noErr)
  3034.         {
  3035.         
  3036.         // create the menu and fill it with all the items
  3037.         // listed in the string list resource
  3038.         
  3039.         contentsMenu = NewMenu(mContents, menuStr);
  3040.         require(contentsMenu != nil, CantCreateContentsMenu);
  3041.         
  3042.         for (index = 1; index < totalItems; index++)
  3043.             {
  3044.             err = TextGetContentsListItem(pData, index, menuStr, nil, nil);
  3045.             require(err == noErr, CantGetItem);
  3046.             
  3047.             AppendMenu(contentsMenu, menuStr);
  3048.             }
  3049.  
  3050.         // add the menu to the menu bar, and redraw the menu bar
  3051.         InsertMenu(contentsMenu, 0);
  3052.         DrawMenuBar();
  3053.         }
  3054.     else
  3055.         {
  3056.         // no contents, do nothing
  3057.         }
  3058.     
  3059.     return;
  3060.     
  3061. // error handling
  3062. CantGetItem:
  3063. CantCreateContentsMenu:
  3064.     
  3065.     if (contentsMenu)     DisposeMenu(contentsMenu);
  3066.  
  3067. ContentsMenuAlreadyInstalled:
  3068.  
  3069.     return;
  3070.         
  3071. } // TextAddContentsMenu
  3072.  
  3073.  
  3074.  
  3075.  
  3076. // TextRemoveContentsMenu removes the contents menu, if any,
  3077. // and redraws the menu bar
  3078.  
  3079. static void TextRemoveContentsMenu(WindowDataPtr pData)
  3080. {
  3081. #pragma unused (pData)
  3082.  
  3083.     MenuHandle    contentsMenu;
  3084.     
  3085.     contentsMenu = GetMenuHandle(mContents);
  3086.     if (contentsMenu)
  3087.         {
  3088.         DeleteMenu(mContents);
  3089.         DisposeMenu(contentsMenu);
  3090.         DrawMenuBar();
  3091.         }
  3092.  
  3093. } // TextRemoveContentsMenu
  3094.  
  3095.  
  3096.  
  3097. // TextGetContentsListItem is a general utility routine for examining the
  3098. // contents menu list, returning the menu and search strings, and returning
  3099. // the total number of entries in the contents list.
  3100. //
  3101. // Pass 0 as itemNum to retrieve the strings for the contents menu title.
  3102. //
  3103. // Pass nil for menuStr, searchStr, or totalItems if you don't want that
  3104. // info returned.
  3105. //
  3106. // Returns eDocumentHasNoContentsEntries if there is no contents string list
  3107. // resource for the specified window
  3108.  
  3109. static OSErr TextGetContentsListItem(WindowDataPtr pData, short itemNum, 
  3110.                               StringPtr menuStr, StringPtr searchStr, 
  3111.                               short *totalItems)
  3112. {
  3113.  
  3114.     OSErr    err;
  3115.     short    oldResFile;
  3116.     short    menuItemNum;
  3117.     short    searchItemNum;
  3118.     Handle    contentsStrListHandle;
  3119.     
  3120.     // if no original resource file, don't bother
  3121.     if (pData->resRefNum == -1)    
  3122.         {
  3123.         return eDocumentHasNoContentsEntries;
  3124.         }
  3125.         
  3126.     err = noErr;
  3127.     
  3128.     oldResFile = CurResFile();
  3129.     UseResFile(pData->resRefNum);
  3130.     
  3131.     // two entries per item
  3132.     //
  3133.     // first (itemNum zero) is content menu title
  3134.     // (second -- itemNum one, search string for menu title -- is unused)
  3135.     
  3136.     menuItemNum = itemNum * 2 + 1;
  3137.     searchItemNum = menuItemNum + 1;
  3138.     
  3139.     contentsStrListHandle = Get1Resource('STR#', kContentsListID);
  3140.     if (contentsStrListHandle)
  3141.         {
  3142.         if (totalItems)    *totalItems = (*(short *)*contentsStrListHandle) / 2;
  3143.  
  3144.         if (menuStr)    GetIndString(menuStr, kContentsListID, menuItemNum);
  3145.         if (searchStr)    
  3146.             {
  3147.             GetIndString(searchStr, kContentsListID, searchItemNum);
  3148.         
  3149.             if (searchStr[0] == 0)
  3150.                 {
  3151.                 // search string was empty, so use the
  3152.                 // menu string as the search string
  3153.                 
  3154.                 GetIndString(searchStr, kContentsListID, menuItemNum);
  3155.                 }
  3156.             }
  3157.         }
  3158.     else
  3159.         {
  3160.         err = eDocumentHasNoContentsEntries;
  3161.         if (totalItems)    *totalItems = 0;
  3162.         }
  3163.     
  3164.     UseResFile(oldResFile);
  3165.     
  3166.     return err;
  3167. } // TextGetContentsListItem
  3168.  
  3169.  
  3170. // TextAdjustContentsMenu enables the items in the contents menu
  3171. //
  3172. // This routine is essentially a placeholder in case the contents
  3173. // menu really were to be dynamically enabled.
  3174.  
  3175. static OSErr TextAdjustContentsMenu(WindowDataPtr pData)
  3176. {
  3177. #pragma unused (pData)
  3178.     
  3179.     EnableCommand(cSelectContents);
  3180.     return(noErr);
  3181.     
  3182. } // TextAdjustContentsMenu
  3183.  
  3184.